Visual basic

Petroutsos ftoc.tex V2 - 01/28/2008 6:12pm Page xiii
Contents at a Glance
Introduction ........................................................ xxix
Chapter 1 • Getting Started with Visual Basic 2008 ........................... 1
Chapter 2 • Variables and Data Types .................................... 35
Chapter 3 • Programming Fundamentals .................................. 85
Chapter 4 • GUI Design and Event-Driven Programming .................... 123
Chapter 5 • The Vista Interface ......................................... 151
Chapter 6 • Basic Windows Controls .................................... 173
Chapter 7 • Working with Forms ....................................... 217
Chapter 8 • More Windows Controls .................................... 267
Chapter 9 • The TreeView and ListView Controls .......................... 305
Chapter 10 • Building Custom Classes ................................... 349
Chapter 11 • Working with Objects ..................................... 395
Chapter 12 • Building Custom Windows Controls ......................... 429
Chapter 13 • Handling Strings, Characters, and Dates ....................... 461
Chapter 14 • Storing Data in Collections ................................. 499
Chapter 15 • Accessing Folders and Files ................................. 541
Chapter 16 • Serialization and XML ..................................... 591
Chapter 17 • Querying Collections and XML with LINQ .................... 621
Chapter 18 • Drawing and Painting with Visual Basic 2008 ................... 649
Chapter 19 • Manipulating Images and Bitmaps ........................... 693
Chapter 20 • Printing with Visual Basic 2008 .............................. 717Petroutsos ffirs.tex V3 - 01/28/2008 6:03pm Page iii
MasteringMicrosoft
Visual Basic 2008
Evangelos Petroutsos
Mark Ridgeway
Wiley Publishing, Inc.Petroutsos ffirs.tex V3 - 01/28/2008 6:03pm Page iiPetroutsos ffirs.tex V3 - 01/28/2008 6:03pm Page i
MasteringMicrosoft
Visual Basic 2008Petroutsos ffirs.tex V3 - 01/28/2008 6:03pm Page iiPetroutsos ffirs.tex V3 - 01/28/2008 6:03pm Page iii
MasteringMicrosoft
Visual Basic 2008
Evangelos Petroutsos
Mark Ridgeway
Wiley Publishing, Inc.Petroutsos ffirs.tex V3 - 01/28/2008 6:03pm Page iv
Acquisitions Editor: Thomas Cirtin
Development Editor: Toni Zaccarini Ackley
Technical Editor: John Mueller
Production Editor: Daniel Scribner
Copy Editor: Sharon Wilkey
Production Manager: Tim Tate
Vice President and Executive Group Publisher: Richard Swadley
Vice President and Executive Publisher: Joseph B. Wikert
Vice President and Publisher: Neil Edde
Book Designer: Maureen Forys and Judy Fung
Proofreader: Nancy Riddiough
Indexer: Ron Strauss
Cover Designer: Ryan Sneed
Cover Image: Pete Gardner / Digital Vision / Getty Images
Copyright © 2008 by Wiley Publishing, Inc., Indianapolis, Indiana
Published simultaneously in Canada
ISBN: 978-0-4701-8742-5
No part of this publication may be reproduced, stored in a retrieval system or transmitted in any form or by any means, electronic,
mechanical, photocopying, recording, scanning or otherwise, except as permitted under Sections 107 or 108 of the 1976 United States
Copyright Act, without either the prior written permission of the Publisher, or authorization through payment of the appropriate
per-copy fee to the Copyright Clearance Center, 222 Rosewood Drive, Danvers, MA 01923, (978) 750–8400, fax (978) 646–8600.
Requests to the Publisher for permission should be addressed to the Legal Department, Wiley Publishing, Inc., 10475 Crosspoint
Blvd., Indianapolis, IN 46256, (317) 572–3447, fax (317) 572–4355, or online at http://www.wiley.com/go/permissions.
Limit of Liability/Disclaimer of Warranty: The publisher and the author make no representations or warranties with respect to
the accuracy or completeness of the contents of this work and specifically disclaim all warranties, including without limitation
warranties of fitness for a particular purpose. No warranty may be created or extended by sales or promotional materials. The advice
and strategies contained herein may not be suitable for every situation. This work is sold with the understanding that the publisher
is not engaged in rendering legal, accounting, or other professional services. If professional assistance is required, the services of a
competent professional person should be sought. Neither the publisher nor the author shall be liable for damages arising herefrom.
The fact that an organization or Website is referred to in this work as a citation and/or a potential source of further information does
not mean that the author or the publisher endorses the information the organization or Website may provide or recommendations it
may make. Further, readers should be aware that Internet Websites listed in this work may have changed or disappeared between
when this work was written and when it is read.
For general information on our other products and services or to obtain technical support, please contact our Customer Care
Department within the U.S. at (800) 762–2974, outside the U.S. at (317) 572–3993 or fax (317) 572–4002.
Wiley also publishes its books in a variety of electronic formats. Some content that appears in print may not be available in electronic
books.
Library of Congress Cataloging-in-Publication Data
Petroutsos, Evangelos.
Mastering Microsoft Visual basic 2008 / Evangelos Petroutsos. — 1st ed.
p. cm.
ISBN 978-0-470-18742-5 (paper/website)
1. Microsoft Visual BASIC. 2. BASIC (Computer program language) I. Title.
QA76.73.B3P492285 2008
005.2
762 — dc22
2007051637
TRADEMARKS: Wiley, the Wiley logo, and the Sybex logo are trademarks or registered trademarks of John Wiley & Sons, Inc.
and/or its affiliates, in the United States and other countries, and may not be used without written permission. All other trademarks
are the property of their respective owners. Wiley Publishing, Inc., is not associated with any product or vendor mentioned in this
book.
10987654321Petroutsos ffirs.tex V3 - 01/28/2008 6:03pm Page v
Dear Reader,
Thank you for choosing Mastering Microsoft Visual Basic 2008. This book is part of a family of
premium quality Sybex books, all written by outstanding authors who combine practical experi-
ence with a gift for teaching.
Sybex was founded in 1976. More than thirty years later, we’re still committed to producing
consistently exceptional books. With each of our titles we’re working hard to set a new standard
for the industry. From the paper we print on, to the authors we work with, our goal is to bring you
the best books available.
I hope you see all that reflected in these pages. I’d be very interested to hear your comments
and get your feedback on how we’re doing. Feel free to let me know what you think about this or
any other Sybex book by sending me an email at nedde@wiley.com, or if you think you’ve found
a technical error in this book, please visit http://sybex.custhelp.com. Customer feedback is
critical to our efforts at Sybex.
Best regards,
Neil Edde
Vice President and Publisher
Sybex, an Imprint of WileyPetroutsos ffirs.tex V3 - 01/28/2008 6:03pm Page viPetroutsos ffirs.tex V3 - 01/28/2008 6:03pm Page vii
To my dearest and most precious ones, Nefeli
and Eleni-Myrsini.Petroutsos ffirs.tex V3 - 01/28/2008 6:03pm Page viiiPetroutsos fack.tex V3 - 01/28/2008 6:06pm Page ix
Acknowledgments
Many people contributed to this book, and I would like to thank themall. I first want to expressmy
deep appreciation to Mark Ridgeway for contributing the Web-related chapters (Chapters 25, 26,
and 27) and Chapter 5, ‘‘The Vista Interface,’’ and to JohnMueller for providing the technical edit of
this book. Iwant to thank the programmers atMicrosoft for their commitment toVisual Basic.Visual
Basic has evolved froma small, limited programming environment to a first-class development tool
for building all types ofWindows andWeb applications, fromsmall hobbyist projects to enterprise
scale applications.
Special thanks to the talented people at Sybex — to all of them and to each one individually.
Starting with editor Toni Zuccarini Ackley, who has taken this book under her wing and improved
it in numerous ways. To acquisitions editor Tom Cirtin, who has followed the progress of the
book, its ups and downs, and managed to coordinate the entire team. To production editor Daniel
Scribner, who kept this project in order and on schedule. Thanks, Daniel. To copyeditor Sharon
Wiley, proofreader Nancy Riddiough, indexer Ron Strauss and everyone else who added their
expertise and talentPetroutsos fack.tex V3 - 01/28/2008 6:06pm Page xPetroutsos fauth.tex V3 - 01/28/2008 6:11pm Page xi
About the Author
Evangelos Petroutsos works as a consultant on medium to large projects, teaches, and writes
articles — but he mostly writes code, VB code. He specializes in VB and SQL, and from the new
technologies, he fancies XML. He has authored many articles and more than 10 programming
books, including the best-selling titles Mastering Microsoft Visual Basic 2005 and Mastering Visual
Basic .NET Database Programming, both published by Sybex.Petroutsos fauth.tex V3 - 01/28/2008 6:11pm Page xiiPetroutsos ftoc.tex V2 - 01/28/2008 6:12pm Page xiv
xiv CONTENTS AT A GLANCE
Chapter 21 • Basic Concepts of Relational Databases ........................ 753
Chapter 22 • Programming with ADO.NET ............................... 805
Chapter 23 • Building Data-Bound Applications ........................... 845
Chapter 24 • Advanced DataSet Operations .............................. 885
Chapter 25 • Building Web Applications ................................. 901
Chapter 26 • ASP.NET 3.5 ............................................ 937
Chapter 27 • ASP.NET Web Services .................................... 981
Appendix A • ’’The Bottom Line’’ ...................................... 997
Appendix B • ’’Debugging and Error Handling’’ .......................... 1045
Index ............................................................. 1075Petroutsos ftoc.tex V2 - 01/28/2008 6:12pm Page xv
Contents
Introduction ........................................................ xxix
Chapter 1 • Getting Started with Visual Basic 2008 .................... 1
Exploring the Integrated Development Environment ........................... 1
The Start Page ....................................................... 2
Starting a New Project ................................................ 3
Using the Windows Form Designer ...................................... 5
Creating Your First VB Application ....................................... 11
Making the Application More User-Friendly .............................. 15
Understanding the IDE Components ...................................... 18
The IDE Menu ...................................................... 18
ToolboxWindow ................................................... 23
Solution Explorer Window ............................................ 23
Properties Window .................................................. 23
Output Window .................................................... 24
Command and Immediate Windows .................................... 24
Error List Window .................................................. 25
Setting Environment Options ............................................ 25
Building a Console Application ........................................... 27
Using Code Snippets ................................................... 29
Using the My Object ................................................... 30
The Bottom Line ...................................................... 33
Chapter 2 • Variables and Data Types ............................ 35
Variables ............................................................ 35
Declaring Variables .................................................. 36
Types of Variables .................................................. 39
The Strict, Explicit, and Infer Options ................................... 54
Object Variables .................................................... 58
VariablesasObjects .................................................... 60
Converting Variable Types ............................................ 61
Formatting Numbers ................................................ 64
User-Defined Data Types ............................................. 66
Examining Variable Types ............................................ 69
Why Declare Variables? .............................................. 71
AVariable’sScope .................................................. 71
A Variable’s Lifetime ................................................ 73
Constants ............................................................ 74Petroutsos ftoc.tex V2 - 01/28/2008 6:12pm Page xvi
xvi CONTENTS
Arrays .............................................................. 75
Declaring Arrays .................................................... 75
Initializing Arrays ................................................... 77
Array Limits ....................................................... 77
Multidimensional Arrays ............................................. 78
Dynamic Arrays .................................................... 81
The Bottom Line ...................................................... 82
Chapter 3 • Programming Fundamentals ......................... 85
Flow-Control Statements ................................................ 85
Decision Statements ................................................. 86
Loop Statements .................................................... 93
Nested Control Structures ............................................ 98
The Exit Statement ................................................. 100
Writing and Using Procedures .......................................... 100
Subroutines ....................................................... 100
Functions ........................................................ 101
Arguments .......................................................... 103
Argument-Passing Mechanisms ....................................... 104
Built-in Functions .................................................. 107
Custom Functions .................................................. 109
Passing Arguments and Returning Values ............................... 111
More Types of Function Return Values ................................. 113
Overloading Functions .............................................. 117
The Bottom Line ..................................................... 121
Chapter 4 • GUI Design and Event-Driven Programming .............. 123
On Designing Windows Applications ..................................... 123
Building a Loan Calculator ............................................. 124
Understanding How the Loan Calculator Application Works ................ 125
Designing the User Interface ......................................... 126
Programming the Loan Application .................................... 129
Validating the Data ................................................. 133
Building a Calculator .................................................. 137
Designing the User Interface ......................................... 137
Programming the MathCalculator ..................................... 139
Using Simple Debugging Tools ....................................... 145
Exception Handling ................................................ 147
The Bottom Line ..................................................... 149
Chapter 5 • The Vista Interface ............................... 151
Introducing XAML ................................................... 151
Introducing the WPF Controls .......................................... 153
Simple ‘‘Hello World’’ WPF Application ................................ 154
Simple Drawing Program ............................................ 156Petroutsos ftoc.tex V2 - 01/28/2008 6:12pm Page xvii
CONTENTS xvii
Data-Binding WPF Controls ............................................ 159
Data-Binding Example 2: Binding to a Database .......................... 164
Creating a WPF Browser Application ..................................... 166
Expression Blend Overview ............................................ 168
The Bottom Line ..................................................... 171
Chapter 6 • BasicWindows Controls ............................ 173
The TextBox Control .................................................. 173
Basic Properties .................................................... 174
Text-Manipulation Properties ......................................... 176
Text-Selection Properties ............................................ 179
Text-Selection Methods .............................................. 180
Undoing Edits ..................................................... 181
VB 2008 at Work: The TextPad Project .................................. 181
Capturing Keystrokes ............................................... 188
Auto-complete Properties ............................................ 190
The ListBox, CheckedListBox, and ComboBox Controls ....................... 195
Basic Properties .................................................... 196
Manipulating the Items Collection ..................................... 197
Selecting Items .................................................... 200
VB 2008 at Work: The ListBox Demo Project ............................. 200
Searching the ListBox ............................................... 203
The ComboBox Control ............................................. 205
The ScrollBar and TrackBar Controls ..................................... 210
The ScrollBar Control ............................................... 210
The TrackBar Control ............................................... 213
The Bottom Line ..................................................... 214
Chapter 7 • Working with Forms .............................. 217
The Appearance of Forms .............................................. 217
Properties of the Form Object ......................................... 218
Placing Controls on Forms ........................................... 223
Setting the TabOrder Property ......................................... 224
VB 2008 at Work: The Contacts Project .................................. 226
Anchoring and Docking ............................................. 229
Splitting Forms into Multiple Panes .................................... 232
The Form’s Events ................................................. 234
Loading and Showing Forms ........................................... 236
The Startup Form .................................................. 237
Controlling One Form from within Another ............................. 238
Forms versus Dialog Boxes ........................................... 240
Building Dynamic Forms at Runtime ..................................... 246
The Form’s Controls Collection ........................................ 247
Creating Event Handlers at Runtime ................................... 253
Designing Menus ..................................................... 255Petroutsos ftoc.tex V2 - 01/28/2008 6:12pm Page xviii
xviii CONTENTS
The Menu Editor ................................................... 255
The ToolStripMenuItem Properties .................................... 257
Manipulating Menus at Runtime ...................................... 260
Iterating a Menu’s Items ............................................. 263
The Bottom Line ..................................................... 265
Chapter 8 • MoreWindows Controls ............................ 267
The Common Dialog Controls ........................................... 267
Using the Common Dialog Controls ................................... 268
The ColorDialog Control ............................................ 270
The FontDialog Control ............................................. 272
The OpenDialog and SaveDialog Controls ............................... 274
The FolderBrowserDialog Control ..................................... 279
The RichTextBox Control ............................................... 283
The RTF Language ................................................. 284
Text Manipulation and Formatting Properties ............................ 286
Methods ......................................................... 289
Advanced Editing Features .......................................... 290
Cutting and Pasting ................................................ 291
Searching in a RichTextBox Control .................................... 291
Handling URLs in the Document ...................................... 292
Displaying a Formatted Directory Listing ............................... 293
VB 2008 at Work: The RTFPad Project .................................. 294
The Bottom Line ..................................................... 302
Chapter 9 • The TreeView and ListView Controls ................... 305
Understanding the ListView, TreeView, and ImageList Controls ............... 305
Tree and List Structures ............................................. 306
The ImageList Control .............................................. 309
The TreeView Control ................................................. 310
Adding Nodes at Design Time ........................................ 312
Adding Nodes at Runtime ........................................... 313
VB 2008 at Work: The TreeViewDemo Project ............................ 316
VB 2008 at Work: The Globe Project .................................... 320
Scanning the TreeView Control ....................................... 328
The ListView Control ................................................. 330
The Columns Collection .............................................. 332
ListView Items and Subitems ......................................... 334
The Items Collection ................................................ 336
The SubItems Collection ............................................. 336
VB 2008 at Work: The ListViewDemo Project ............................ 337
Sorting the ListView Control ......................................... 340
Processing Selected Items ............................................ 342
VB 2008 at Work: The CustomExplorer Project ........................... 343
Additional Topics .................................................. 346
The Bottom Line ..................................................... 347Petroutsos ftoc.tex V2 - 01/28/2008 6:12pm Page xix
CONTENTS xix
Chapter 10 • Building Custom Classes ........................... 349
Classes and Objects ................................................... 349
What Is a Class? ...................................................... 350
Classes Combine Code with Data ...................................... 351
Building the Minimal Class ............................................. 352
Adding Code to the Minimal Class .................................... 355
Using Property Procedures ........................................... 356
Customizing Default Members ........................................ 362
Custom Enumerations .............................................. 366
Object Constructors ................................................ 375
Using the SimpleClass in Other Projects ................................ 376
Firing Events ...................................................... 378
Instance and Shared Methods ......................................... 380
A ‘‘Real’’ Class ....................................................... 384
Parsing a Filename ................................................. 384
Converting Numbers to Strings ....................................... 385
Operator Overloading ................................................. 388
VB 2008 at Work: The LengthUnits Class ................................ 389
The Bottom Line ..................................................... 394
Chapter 11 • Working with Objects ............................. 395
Issues in Object-Oriented Programming ................................... 395
Classes versus Objects .............................................. 395
Objects versus Object Variables ....................................... 396
Properties versus Fields ............................................. 400
Shared versus Instance Members ...................................... 401
Type Casting ...................................................... 402
Early versus Late Binding ............................................ 403
Discovering a Variable’s Type ........................................ 404
Inheritance .......................................................... 404
How to Apply Inheritance ........................................... 405
Polymorphism ....................................................... 415
Building the Shape Class ............................................ 417
Who Can Inherit What? ................................................ 421
Parent Class Keywords .............................................. 422
Derived Class Keywords ............................................ 422
Parent Class Member Keywords ...................................... 423
Derived Class Member Keyword ...................................... 423
VB 2008 At Work: The InheritanceKeywords Project ....................... 424
MyBase and MyClass ............................................... 425
The Class Diagram Designer ......................................... 427
The Bottom Line ..................................................... 428
Chapter 12 • Building CustomWindows Controls ................... 429
On Designing Windows Controls ........................................ 429
Enhancing Existing Controls ............................................ 430Petroutsos ftoc.tex V2 - 01/28/2008 6:12pm Page xx
xx CONTENTS
Building the FocusedTextBox Control .................................. 431
Building Compound Controls ........................................... 438
VB 2008 at Work: The ColorEdit Control ................................ 439
Building User-Drawn Controls .......................................... 442
VB 2008 at Work: The Label3D Control ................................. 443
Raising Custom Events .............................................. 450
Using the Custom Control in Other Projects ............................. 452
Designing Irregularly Shaped Controls .................................... 453
Customizing List Controls .............................................. 456
Designing Owner-Drawn ListBox Controls .............................. 457
The Bottom Line ..................................................... 459
Chapter 13 • Handling Strings, Characters, and Dates ................ 461
Handling Strings and Characters ........................................ 461
The Char Class .................................................... 462
The String Class ................................................... 464
The StringBuilder Class ............................................. 473
VB 2008 at Work: The StringReversal Project ............................. 476
VB 2008 at Work: The CountWords Project .............................. 479
Handling Dates and Times ............................................. 481
The DateTime Class ................................................ 481
The TimeSpan Class ................................................ 492
The Bottom Line ..................................................... 497
Chapter 14 • Storing Data in Collections ......................... 499
Advanced Array Topics ................................................ 499
Sorting Arrays ..................................................... 499
Searching Arrays .................................................. 502
Performing Other Array Operations ................................... 506
Array Limitations .................................................. 509
The ArrayList Collection ............................................... 509
Creating an ArrayList ............................................... 509
Adding and Removing ArrayList Items ................................. 510
Sorting ArrayLists .................................................. 513
Searching ArrayLists ............................................... 515
Iterating an ArrayList ............................................... 515
The HashTable Collection .............................................. 516
VB 2008 at Work: The WordFrequencies Project .......................... 518
The SortedList Collection .............................................. 523
VB 2008 at Work: The SortedList Project ................................ 524
Other Collections ..................................................... 527
The IEnumerator and IComparer Interfaces ................................ 528
Enumerating Collections ............................................ 528
Custom Sorting .................................................... 531
Generic Collections ................................................... 537
The Bottom Line ..................................................... 539Petroutsos ftoc.tex V2 - 01/28/2008 6:12pm Page xxi
CONTENTS xxi
Chapter 15 • Accessing Folders and Files ......................... 541
The IO Namespace and the FileSystem Component .......................... 541
Using the My.Computer.FileSystem Component ............................ 542
Manipulating Folders and Files with the IO Namespace ...................... 546
The Directory Class ................................................. 547
The File Class ..................................................... 554
Drive, Folder, and File Properties ...................................... 560
The Path Class .................................................... 564
VB 2008 at Work: The CustomExplorer Project ........................... 566
Accessing Files ....................................................... 569
Using Streams ..................................................... 570
VB 2008 at Work: The RecordSave Project ............................... 580
The FileSystemWatcher Component ...................................... 585
Properties ........................................................ 585
Events ........................................................... 586
VB 2008 at Work: The FileSystemWatcher Project ......................... 587
The Bottom Line ..................................................... 589
Chapter 16 • Serialization and XML ............................ 591
Understanding Serialization Types ....................................... 591
Using Binary and SOAP Serialization ..................................... 592
Serializing Individual Objects ......................................... 592
Deserializing Individual Objects ...................................... 594
Serializing Collections .............................................. 595
Deserializing Collections ............................................ 596
Persisting a HashTable ................................................ 598
Persisting a TreeView’s Nodes .......................................... 601
Using XML Serialization ............................................... 607
Serializing and Deserializing Individual Objects .......................... 608
Serializing Custom Objects ........................................... 609
Serializing ArrayLists and HashTables ................................. 613
Working with XML Files ............................................... 615
Understanding XML Structure ........................................ 615
Editing XML Files .................................................. 617
The Bottom Line ..................................................... 620
Chapter 17 • Querying Collections and XML with LINQ ............... 621
What Is LINQ? ....................................................... 621
LINQ Components ................................................. 623
LINQ to Objects ...................................................... 625
Querying Collections ............................................... 627
Aggregating with LINQ ............................................. 630
LINQ to XML ........................................................ 631
Traversing XML Documents .......................................... 633
Adding Dynamic Content to an XML Document .......................... 634
LINQ to SQL ........................................................ 642Petroutsos ftoc.tex V2 - 01/28/2008 6:12pm Page xxii
xxii CONTENTS
Retrieving Data with the ExecuteQuery Method ........................... 646
The Bottom Line ..................................................... 647
Chapter 18 • Drawing and Painting with Visual Basic 2008 ............ 649
Displaying and Sizing Images ........................................... 649
Drawing with GDI+ ................................................... 652
The Basic Drawing Objects ........................................... 654
Drawing Shapes ................................................... 663
Drawing Methods .................................................. 667
Gradients ........................................................ 681
Clipping ......................................................... 685
Applying Transformations ............................................. 687
VB 2008 at Work: The ImageCube Project ............................... 690
VB 2008 at Work: Plotting Functions ................................... 691
The Bottom Line ..................................................... 691
Chapter 19 • Manipulating Images and Bitmaps ................... 693
Specifying Colors ..................................................... 693
The RGB Color Cube ............................................... 694
Defining Colors .................................................... 695
The Image Object ..................................................... 699
Properties ........................................................ 699
Methods ......................................................... 699
VB 2008 at Work: The Thumbnails Project ............................... 702
Exchanging Images through the Clipboard .............................. 705
The Bitmap Object .................................................... 706
Processing Bitmaps ................................................. 708
VB 2008 at Work: The ImageProcessing Project ........................... 710
The Bottom Line ..................................................... 715
Chapter 20 • Printing with Visual Basic 2008 ...................... 717
The Printing Components .............................................. 717
The PrintDocument Control .......................................... 717
The PrintDialog Control ............................................. 720
The PageSetupDialog Control ........................................ 721
The PrintPreviewDialog Control ...................................... 723
Printer and Page Properties ............................................. 725
Retrieving the Printer Names ......................................... 726
Page Geometry .................................................... 728
VB 2008 at Work: The SimplePrintout Project ............................ 729
VB 2008 at Work: The PageSettings Project .............................. 732
Practical Printing Examples ............................................. 737
Printing Tabular Data ............................................... 738
Printing Plain Text ................................................. 744
Printing Bitmaps ................................................... 748
The Bottom Line ..................................................... 751Petroutsos ftoc.tex V2 - 01/28/2008 6:12pm Page xxiii
CONTENTS xxiii
Chapter 21 • Basic Concepts of Relational Databases ................. 753
What Is a Database? ................................................... 753
Using Relational Databases .......................................... 754
Obtaining the Northwind and Pubs Sample Databases ..................... 755
Exploring the Northwind Database .................................... 756
Exploring the Pubs Database ......................................... 760
Understanding Relations ............................................ 761
Server Explorer ...................................................... 763
Working with Tables ............................................... 767
Working with Relationships, Indices, and Constraints ..................... 770
Structured Query Language ............................................ 773
Executing SQL Statements ........................................... 775
Using Selection Queries ............................................. 776
Working with Calculated Fields ....................................... 783
Calculating Aggregates .............................................. 783
Using SQL Joins ................................................... 785
Grouping Rows .................................................... 788
Limiting Groups with HAVING ....................................... 790
Selecting Groups with IN and NOT IN.................................. 791
Selecting Ranges with BETWEEN ...................................... 791
Action Queries ....................................................... 791
Deleting Rows ..................................................... 792
Inserting New Rows ................................................ 793
Editing Existing Rows ............................................... 794
The Query Builder .................................................... 794
The Query Builder Interface .......................................... 795
SQL at Work: Calculating Sums ....................................... 796
SQL at Work: Counting Rows ........................................ 797
Parameterized Queries .............................................. 798
Calculated Columns ................................................ 799
Stored Procedures .................................................... 800
The SalesByCategory Stored Procedure ................................. 801
The Bottom Line ..................................................... 802
Chapter 22 • Programming with ADO.NET ....................... 805
Stream- versus Set-Based Data Access .................................... 805
The Basic Data-Access Classes ........................................... 806
The Connection Class ............................................... 807
The Command Class ................................................ 809
The DataReader Class ............................................... 819
Storing Data in DataSets ............................................... 822
Filling DataSets .................................................... 823
Accessing the DataSet’s Tables ........................................ 828
Working with Rows ................................................ 828
Handling Null Values ............................................... 829
Adding and Deleting Rows .......................................... 830Petroutsos ftoc.tex V2 - 01/28/2008 6:12pm Page xxiv
xxiv CONTENTS
Navigating through a DataSet ........................................ 831
Update Operations ................................................... 834
Updating the Database with the DataAdapter ............................ 835
Handling Identity Columns .......................................... 836
VB 2008 at Work: The SimpleDataSet Project ............................... 837
The Bottom Line ..................................................... 843
Chapter 23 • Building Data-Bound Applications .................... 845
Working with Typed DataSets .......................................... 845
Generating a Typed DataSet .......................................... 846
Exploring the Typed DataSet ......................................... 850
Data Binding ........................................................ 855
Using the BindingSource Class ........................................ 857
Designing Data-Driven Interfaces the Easy Way ............................ 863
Enhancing the Navigational Tools ..................................... 866
Binding Hierarchical Tables .......................................... 867
Adjusting the Appearance of the DataGridView Control ................... 870
Building More-Functional Interfaces ................................... 877
The Bottom Line ..................................................... 883
Chapter 24 • Advanced DataSet Operations ....................... 885
Working with SQL Expressions .......................................... 885
Selecting Rows .................................................... 886
Simple Calculated Columns ............................................ 887
Calculated Columns with Aggregates .................................. 888
VB 2008 at Work: The SQL Expressions Project ............................. 890
Selecting and Viewing an Order’s Details ............................... 895
The Bottom Line ..................................................... 900
Chapter 25 • BuildingWeb Applications ......................... 901
Developing for the Web ................................................ 901
Understanding HTML and XHTML ...................................... 902
Working with HTML .................................................. 903
Page Construction .................................................. 904
Text Management .................................................. 905
Horizontal Rules ................................................... 905
Images ........................................................... 905
Links ............................................................ 906
Embedding Media ................................................. 906
Comments ........................................................ 906
Scripts ........................................................... 907
Lists ............................................................. 907
Tables ........................................................... 907
Page Formatting ................................................... 909
Forms and Form Elements ........................................... 909
Cascading Style Sheets (CSS) ............................................ 911Petroutsos ftoc.tex V2 - 01/28/2008 6:12pm Page xxv
CONTENTS xxv
Formatting Styles with CSS .......................................... 912
Page Formatting with CSS ........................................... 913
JavaScript ........................................................... 916
AJAX ............................................................ 918
Microformats ........................................................ 918
Server-Side Technologies ............................................... 918
Creating a Web Application ............................................ 919
Controls ............................................................ 922
Standard Controls .................................................. 922
Data Controls ..................................................... 924
Validation Controls ................................................. 924
Navigation Controls ................................................ 925
Login Controls .................................................... 925
WebParts Controls ................................................. 926
AJAX Extensions Controls ........................................... 926
Reporting Controls ................................................. 928
HTML Controls .................................................... 928
Maintaining State ..................................................... 929
Master Pages ........................................................ 930
ASP.NET Objects ..................................................... 931
Application Object ................................................. 931
Context Object .................................................... 931
Request Object .................................................... 931
Response Object ................................................... 932
Server Object ...................................................... 932
Session Object ..................................................... 934
Trace Object ...................................................... 934
Postback ............................................................ 935
The Bottom Line ..................................................... 935
Chapter 26 • ASP.NET 3.5 ................................... 937
Planning the Demonstration Site ......................................... 937
Getting Started .................................................... 938
Building the Style Sheet for MasterPage.master .............................. 939
Creating the Style Sheet ............................................. 940
Attaching the Style Sheet to the Master Page ............................. 942
Creating the Content Master Page ........................................ 944
Creating ContentStyleSheet.css ......................................... 945
Completing ContentMasterPage.master .................................. 947
Adding Elements to the Main Master Page ................................. 948
Creating the Web User Control ....................................... 949
Adding the Web User Control to Your Page ............................. 952
Creating the Footer.ascx Web User Control ............................... 953
Building the Site Navigation ............................................ 955
Creating a SiteMap ................................................. 955
Configuring the Menu Control for MasterPage.master ...................... 955Petroutsos ftoc.tex V2 - 01/28/2008 6:12pm Page xxvi
xxvi CONTENTS
Creating the Navbar in ContentsMasterPage.master ......................... 956
Adding Authentication ................................................ 957
Using the Login Control ............................................. 957
Establishing Forms-Based Authentication ............................... 957
Adding an Access Rule .............................................. 958
Adding a LoginName Control to MasterPage.master ........................ 959
Adding a LoginName Control to ContentMasterPage.master .................. 960
Adding Content Pages ................................................. 960
Adding an Entry to the SiteMap ....................................... 961
Updating the SiteMapDataSource Control ............................... 961
Running the Application ............................................ 961
Adding Further Content Pages .......................................... 963
Adding Items to the SiteMap ......................................... 963
Using Buttons for Navigation ......................................... 963
Building the Password Page .......................................... 965
Building the NewUser.aspx page ....................................... 967
Working with Data ................................................... 967
Creating the XML Database .......................................... 968
Working with the GridView Control ................................... 970
Further Configuration of the GridView Control .......................... 971
Building the Computers.aspx Page ......................................... 974
Adding the DropDownList Control .................................... 975
Adding the DetailsView Control ...................................... 976
Further Configuring the DetailsView Control ............................ 977
Building the Report.aspx page ............................................ 978
Adding the MicrosoftReportViewer Control ............................. 978
Creating the Report ................................................. 978
The Bottom Line ..................................................... 980
Chapter 27 • ASP.NETWeb Services ............................ 981
Using ASP.NET Web Services and WCF ................................... 981
ASP.NET Web Services .............................................. 981
Windows Communication Foundation (WCF) ............................ 982
Understanding Technologies Associated with Web Services ................... 982
SOAP ........................................................... 983
Web Services Description Language (WSDL) ............................. 983
SOAP Discovery ................................................... 983
Universal Description, Discovery, and Integration (UDDI) .................. 983
Creating a Simple ASP.NET Web Service .................................. 984
Setting Up the Web Service .......................................... 984
Testing the Web Service ............................................. 985
Consuming the Web Service .......................................... 986
Developing a Stand-Alone Web Service ................................... 986
Building MyWebService ............................................. 987
Deploying MyWebService ........................................... 989
Consuming MyWebService .......................................... 989Petroutsos ftoc.tex V2 - 01/28/2008 6:12pm Page xxvii
CONTENTS xxvii
Simple AJAX Implementation ........................................... 993
The Bottom Line ..................................................... 995
Appendix A • ............................................ 997
Appendix B • ........................................... 1045
Index .............................................................. 1075Petroutsos ftoc.tex V2 - 01/28/2008 6:12pm Page xxviiiPetroutsos flast.tex V3 - 01/28/2008 6:13pm Page xxix
Introduction
Welcome to Visual Basic 2008, the most mature version yet of the most popular programming
language for building Windows and web applications. In modern software development, how-
ever, the language is only one of the components we use to build applications. The most important
component is the .NET Framework, which is an indispensable component of every application; it’s
actually more important than the language itself. You can think of the Framework as an enormous
collection of functions for just about any programming task. All drawing methods, for example,
are part of the System.Drawing class. To draw a rectangle, you call the DrawRectangle method of
the System.Drawing class, passing the appropriate arguments. To create a new folder, you call the
CreateDirectory method of the Directory class, and to retrieve the files in a folder you call the
GetFiles method of the same class.
The Framework contains all the functionality of the operating system and makes it available to
your application through methods. The language and the Framework are the two ‘‘programming’’
components, absolutely necessary to build Windows applications. It’s possible to develop appli-
cations with these two components alone, but the process would be awfully slow. The software
development process relies on numerous tools that streamline the coding experience, and these
tools are provided for us by Visual Studio 2008.
The third component is an integrated environment that hosts a number of tools enabling you
to perform many common tasks with point-and-click operations. It’s basically an environment
in which you can design your forms with visual tools and write code as well. This environment,
provided by Visual Studio 2008, is known as an integrated development environment, or IDE.
You’ll be amazed by the functionality provided by the tools of Visual Studio 2008: you can actually
design a functional data-driven application without writing a single line of code. You can use
similar tools in the same environment to design a fancy data-driven web page without a single line
of code. Visual Studio even provides tools for manipulating databases and allows you to switch
between tasks, all in the same, streamlined environment. You realize, of course, that Visual Studio
isn’t about writing applications without code; it just simplifies certain tasks through wizards and,
more often than not, we step in and provide custom code to write a functional application. Even
so, Visual Studio 2008 provides numerous tools, from debugging tools to help you track and fix
all kinds of bugs in your code, to database manipulation tools.
This book shows you how to use Visual Studio 2008 and Visual Basic 2008 to design rich Win-
dows and web applications. We’ll start with the visual tools and then we’ll explore Visual Basic
and the Framework. AWindows application consists of a visual interface and code behind the ele-
ments of the interface. (The code handles the user actions on the visual interface, such as the click
of a button, the selection of a menu item, and so on.) You’ll use the tools of Visual Studio to build
the visual interface and then you’ll program the elements of the application with Visual Basic. For
any nontrivial processing, such as file and folder manipulation, data storage, and so on, you’ll usePetroutsos flast.tex V3 - 01/28/2008 6:13pm Page xxx
xxx INTRODUCTION
the appropriate classes of the .NET Framework. A substantial segment of this book deals with the
most useful components of the Framework.
TheMastering Series
The Mastering series from Sybex provides outstanding instruction for readers with intermediate
and advanced skills, in the form of top-notch training and development for those already working
in their field and clear, serious education for those aspiring to become pros. Every Mastering book
includes the following:
◆ Real-World Scenarios, ranging from case studies to interviews, that show how the tool,
technique, or knowledge presented is applied in actual practice
◆ Skill-based instruction, with chapters organized around real tasks rather than abstract
concepts or subjects.
◆ Self-review test questions, so you can be certain you’re equipped to do the job right.
Who Should Read This Book?
You don’t need a solid knowledge of Visual Basic to read this book, but you do need a basic
understanding of programming. You need to know the meaning of variables and functions and
how an If...Then structure works. This book is aimed at the typical programmer who wants to
get the most out of Visual Basic. It covers the topics I felt are of use to most VB programmers,
and it does so in depth. Visual Basic 2008 and the .NET Framework 3.5 are two extremely rich
programming tools, and I had to choose between a superficial coverage of many topics and an
in-depth coverage of fewer topics. To make room for more topics, I have avoided including a lot
of reference material and lengthy listings. For example, you won’t find complete project listings or
form descriptions. I assume that you can draw a few controls on a form and set their properties,
and that you don’t need long descriptions of the controls’ properties. I’m also assuming that you
don’t want to read the trivial segments of each application. Instead, the listings concentrate on the
‘‘meaty’’ part of the code: the procedures that explain the topic at hand.
The topics covered in this book were chosen to provide a solid understanding of the principles
and techniques for developing applications with Visual Basic. Programming isn’t about new
keywords and functions. I chose the topics I felt every programmer should learn in order to master
the language. I was also motivated by my desire to present useful, practical examples. You will
not find all topics equally interesting or important. My hope is that everyone will find something
interesting and something of value for his or her daily work— whether it’s an application that
maps the folders and files of a drive to a TreeView control, an application that prints tabular
data, a data-driven application for editing customers or products, or an application that saves a
collection of objects to a file.
Many books offer their readers long, numbered sequences of steps to accomplish a task. Follow-
ing instructions simplifies certain tasks, but programming isn’t about following instructions. It’s
about being creative; it’s about understanding principles and being able to apply the same tech-
niques in several practical situations. And the way to creatively exploit the power of a language
such as Visual Basic 2008 is to understand its principles and its programming model.
In many cases, I provide a detailed, step-by-step procedure that will help you accomplish a
task, such as designing a menu, for example. But not all tasks are as simple as designing menus.
I explain why things must be done in a certain way, and I present alternatives and try to connectPetroutsos flast.tex V3 - 01/28/2008 6:13pm Page xxxi
INTRODUCTION xxxi
new topics to those explained earlier in the book. In several chapters, I expand on applications
developed in earlier chapters. Associating new knowledge with something you have mastered
already provides positive feedback and a deeper understanding of the language.
This book isn’t about the hottest features of the language; it’s about solid programming tech-
niques and practical examples. After youmaster the basics of programmingWindows applications
with Visual Basic 2008 and you feel comfortable with the more advanced examples of the book,
you will find it easy to catch up with the topics not discussed in this book. Of course, you will find
information about the latest data access techniques,aswellasanintroductiontoLINQ(Language
Integrated Query), which is the hottest new component of the Framework.
Howabout theAdvanced Topics?
Some of the topics discussed in this book are nontrivial, and quite a few topics can be considered
advanced. The TreeView control, for example, is not a trivial control, like the button or text box
control, but it’s ideal for displaying hierarchical information. (This is the control that displays
the hierarchy of folders in Windows Explorer.) If you want to build an elaborate user interface,
you should be able to program controls such as the TreeView and ListView controls, which are
discussed in Chapter 9, ‘‘The TreeView and ListView Controls.’’
You may also find some examples to be more difficult than you expected. I have tried to
make the text and the examples easy to read and understand, but not unrealistically simple.
In Chapter 15, ‘‘Accessing Folders and Files,’’ you will find information about the File and
Directory objects. You can use these objects to access and manipulate the file system from within
your application, but this chapter wouldn’t be nearly as useful without an application that shows
you how to scan a folder recursively (scan the folder’s files and then its subfolders, to any depth).
To make each chapter as useful as I could, I’ve included nontrivial examples, which will provide a
better understanding of the topics. In addition, many of these examples can be easily incorporated
into your applications.
You can do a lot with the TreeView control with very little programming, but to make
the most out of this control, you must be ready for some advanced programming — nothing
terribly complicated, but some things just aren’t trivial. Programming most of the operations
of the TreeView control, for instance, is not complicated, but if your application calls for
populating a TreeView control with an arbitrary number of branches (such as mapping a direc-
tory structure to a TreeView control), the code can get complex. The same goes for printing; it’s
fairly straightforward to write a program that prints some text, but printing tabular reports takes
substantial coding effort.
The reason I’ve included the more advanced examples is that the corresponding chapters
would be incomplete without them. If you find some material to be over your head at first read-
ing, you can skip it and come back to it after you have mastered other aspects of the language.
But don’t let a few advanced examples intimidate you. Most of the techniques are well within
the reach of an average VB programmer. The few advanced topics were included for the readers
who are willing to take that extra step and build elaborate interfaces by using the latest tools and
techniques.
There’s another good reason for including advanced topics. Explaining a simple topic, such as
how to populate a collection with items, is very simple. But what good is it to populate a collection
if you don’t know how to save it to disk and read back its items in a later session? Likewise, what
good is it to learn how to print simple text files? In a business environment, you will most likely
be asked to print a tabular report, which is substantially more complicated than printing text. In
Chapter 20, ‘‘Printing with Visual Basic 2008,’’ you will learn how to print business reports withPetroutsos flast.tex V3 - 01/28/2008 6:13pm Page xxxii
xxxii INTRODUCTION
headers, footers, and page numbers, and even how to draw grids around the rows and columns
of the report. One of my goals in writing this book was to exhaust the topics I’ve chosen to discuss
and present all the information you need to do something practical.
The Structure of the Book
This book isn’t meant to be read from cover to cover, and I know that most people don’t read
computer books this way. Each chapter is independent of the others, although all chapters contain
references to other chapters. Each topic is covered in depth; however, I make no assumptions
about the reader’s knowledge of the topic. As a result, you may find the introductory sections
of a chapter too simple. The topics become progressively more advanced, and even experienced
programmers will find some new information in most chapters. Even if you are familiar with
the topics in a chapter, take a look at the examples. I have tried to simplify many of the advanced
topics and demonstrate them with clear, practical examples.
This book tries to teach through examples. Isolated topics are demonstrated with short
examples, and at the end of many chapters you’ll build a large, practical application (a real-world
application) that ‘‘puts together’’ the topics and techniques discussed throughout the chapter.
You may find some of the more advanced applications a bit more difficult to understand, but you
shouldn’t give up. Simpler applications would have made my job easier, but the book wouldn’t
deserve the Mastering title, and your knowledge of Visual Basic wouldn’t be as complete.
The book starts with the fundamentals of Visual Basic 2008. You’ll learn how to design
visual interfaces with point-and-click operations and how to program a few simple events, such as
the click of the mouse on a button. After reading the first two chapters, you’ll understand the
structure of a Windows application. Then you’ll explore the elements of the visual interface
(the basicWindows controls) and how to program them. You’ll also learn about theMy object and
code snippets, two features that make Visual Basic so simple and fun to use. These two objects will
also ease the learning process and make it much simpler to learn the features of the language.
I then discuss in detail the basic components of Windows applications. I explain the most
common controls you’ll use in building Windows forms in detail, as well as how to work with
forms: how to design forms, how to design menus for your forms, how to create applications with
multiple forms, and so on. You will find detailed discussions of many Windows controls, as well
as how to take advantage of the built-in dialog boxes, such as the Font and Color dialog boxes, in
your applications.
Visual Basic 2008 is a truly object-oriented language, and objects are the recurring theme
in every chapter. The three following chapters (chapter 10, 11 and 12) contain a formal and
more systematic treatment of objects. You will learn how to build custom classes and controls,
which will help you understand object-oriented programming a little better. You will also learn
about inheritance and will see how easy it is to add custom functionality to existing classes
through inheritance.
The following few chapters deal with some of the most common classes of the .NET
Framework. The Framework is at the very heart of Windows programming; it’s your gateway
to the functionality of the operating system itself, and it’s going to be incorporated into the next
version of Windows. You’ll examine several extremely interesting topics such as collections (for
example, ArrayLists and HashTables), the classes for manipulating files and folders, the String-
Builder class that manipulates text, XML serialization, and a few more, including the Language
Integrated Query component (LINQ, which is brand new to the latest version of the Framework).
Then you will find a few chapters on graphics. You’ll learn how to use the classes of the
Framework that generate graphics, and you’ll learn how to create vector drawings as well asPetroutsos flast.tex V3 - 01/28/2008 6:13pm Page xxxiii
INTRODUCTION xxxiii
how to manipulate bitmaps. In Chapter 20, you’ll learn everything you need to create printouts
with Visual Basic 2008 and see a few practical examples.
The first twenty chapters deal with the fundamentals of the language and Windows applica-
tions. Following these chapters, you will find an overview of the data-access tools. The emphasis
is on the visual tools, and you will learn how to query databases and present data to the user.
You will also find information on programming the basic objects of ADO.NET and write simple
data-driven Windows applications.
In the last few chapters of this book you will learn about web applications, the basics of
ASP.NET 2, how to develop data-bound web applications, and how to write web services.
Downloading This Book’s Code
The code for the examples andprojects canbedownloadedfromthe Sybexwebsite (www.sybex.com).
At the main page, you can find the book’s page by searching for the author, the title, or the ISBN
(9780470187425), and then clicking the book’s link listed in the search results. On the book’s page,
click the Download link. It will take you to the download page. The downloaded source code is a
zip file, which you can unzip with the WinZip utility.
How to Reach the Author
Despite our best efforts, a book of this size is bound to contain errors. Although a printed medium
isn’t as easy to update as a website, I will spare no effort to fix every problem you report (or I dis-
cover). The revised applications, along with any other material I think will be of use to the readers of
this book, will be posted on the Sybex website. If you have any problems with the text or the applica-
tions in this book, you can contact me directly at pevangelos@yahoo.com.
Although I can’t promise a response to every question, I will fix any problems in the examples and
provide updated versions. I would also like to hear any comments you may have on the book, about
the topics you liked or did not like, and how useful the examples are. Your comments will be taken
into consideration in future editions.Petroutsos flast.tex V3 - 01/28/2008 6:13pm Page xxxivPetroutsos c01.tex V2 - 01/28/2008 11:43am Page 1
Chapter 1
Getting Started with
Visual Basic 2008
I’m assuming that you have installed one of the several versions of Visual Studio 2008. For this
book, I used the Professional Edition of Visual Studio, but just about everything discussed in
this book applies to the Standard Edition as well. Some of the features of the Professional Edition
that are not supported by the Standard Edition concern database tools, which are discussed in
Chapters 21 through 24 of this book.
You may have even already explored the new environment on your own, but this book starts
with an overview of Visual Studio and its basic tools. It doesn’t even require any knowledge of
VB 6, just some familiarity with programming at large. As you already know, Visual Basic 2008
is just one of the languages you can use to build applications with Visual Studio 2008. I happen
to be convinced that it is also the simplest, most convenient language, but this isn’t really the
issue; I’m assuming you have your reasons to code in VB, or else you wouldn’t be reading this
book. What you should keep in mind is that Visual Studio 2008 is an integrated environment for
building, testing, debugging, and deploying a variety of applications: Windows applications, web
applications, classes and custom controls, and even console applications. It provides numerous
tools for automating the development process, visual tools for performing many common design
and programming tasks, and more features than any author would hope to cover.
In this chapter, you’ll learn how to do the following:
◆ Navigate the integrated development environment of Visual Studio
◆ Understand the basics of aWindows application
Exploring the IntegratedDevelopment Environment
Visual Basic 2008 is just one of the languages you can use to program your applications. The
language is only one aspect of a Windows application. The visual interface of the application isn’t
tied to a specific language, and the same tools you’ll use to develop your application’s interface
will also be used by all programmers, regardless of the language they’ll use to code the application.
To simplify the process of application development, Visual Studio provides an environment
that’s common to all languages, which is known as an integrated development environment (IDE).
The purpose of the IDE is to enable the developer to do as much as possible with visual tools,
before writing code.
The IDE provides tools for designing, executing, and debugging your applications. It will be
a while before you explore all the elements of the IDE, and I will explain the various items as
needed in the course of the book. In this section, you’ll look at the basic components of the IDEPetroutsos c01.tex V2 - 01/28/2008 11:43am Page 2
2 CHAPTER 1 GETTING STARTED WITH VISUAL BASIC 2008
needed to build simple Windows applications. You’ll learn how its tools allow you to quickly
design the user interface of your application, as well as how to program the application.
The IDE is your second desktop, and you’ll be spending most of your productive hours in this
environment.
The Start Page
When you run Visual Studio 2008 for the first time, you will be prompted to select the type of
projects you plan to build with Visual Studio, so that the environment can be optimized for that
specific type of development. I’m assuming that you have initially selected the Visual Basic Devel-
opment settings, which will optimize your copy of Visual Studio for building Windows and web
applications with Visual Basic 2008. You can always change these settings, as explained at the end
of this section.
After the initial configuration, you will see a window similar to the one shown in Figure 1.1. The
Recent Projects pane will be empty, of course, unless you have already created some test projects.
Visual Studio 2008 will detect the settings of a previous installation, so if you’re upgrading from
an earlier version of Visual Studio, the initial screen will not be identical to the one shown in
Figure 1.1.
Figure 1.1
This is what you’ll see
when you start Visual
Studio for the first time.
On the Start Page of Visual Studio, you will see the following panes:
Recent Projects Here you see a list of the projects you opened most recently with Visual
Studio, and you can select the one you want to open again— chances are that you will con-
tinue working on the same project as the last time. Each project’s name is a hyperlink, and you
can open it by clicking its name. At the bottom of the Recent Projects section are two hyper-
links, for opening or creating another project.
MSDN: Visual Studio This section is a browser window that displays an MSDN
(the Microsoft Developer Network, which is the definitive resource for all Microsoft tech-
nologies and products) page when the computer is connected to the Internet. In this section,Petroutsos c01.tex V2 - 01/28/2008 11:43am Page 3
EXPLORING THE INTEGRATED DEVELOPMENT ENVIRONMENT 3
you will see news about Visual Studio, the supported languages, articles, and other inter-
esting bits of information.
Getting Started This section contains links to basic programming tasks in the product’s
documentation.
Visual Studio Headlines This section contains links to announcements and other news
of interest to VB developers.
Most developers will skip the Start Page. To do so, open the Tools menu and choose the
Import And Export Settings command to start a configuration wizard. In the first dialog box of
the wizard, select the Reset All Settings check box and click the Next button. The next screen
of the wizard prompts you for the location where the new settings will be saved, so that Visual
Studio can read them every time it starts. Leave the default location as is and click Next again to
see the last screen of the wizard, in which you’re prompted to select a default collection of settings.
This collection depends on the options you’ve installed on your system. I installed Visual
Studio 2008 with Visual Basic only on my system, and I was offered the following options: General
Development Settings, Visual Basic Development Settings, and Web Development Settings.
For the default configuration of my copy of Visual Studio, and for the purposes of this book, I
chose the Visual Basic Development Settings, so that Visual Studio could optimize the environ-
ment for a typical VB developer. Click the Finish button to see a summary of the process and then
close the wizard.
Starting a New Project
At this point, you can create a new project and start working with Visual Studio. To best explain
the various items of the IDE, we will build a simple form. The form is the window of your
application — it’s what users will see on their Desktop when they run your application.
Open the File menu and choose New Project, or click Create Project/Solution in the Start Page.
In the New Project dialog box that pops up (see Figure 1.2), you’ll see a list of project types you can
create with Visual Studio. The most important ones are Windows Forms Applications, which are
typical Windows applications with one or more forms (windows); Console Applications, which
are simple applications that interact with the user through a text window (the console); Windows
Forms Control Libraries, which are collections of custom controls; and Class Libraries, which are
collections of classes. These are the project types we’ll cover in depth in this book.
If you have installed Visual Basic 2008 Express Edition, you will see fewer project types in the
New Project dialog box, but the projects discussed in this book are included.
Notice the Create Directory For Solution check box in the dialog box of Figure 1.2. By default,
Visual Studio creates a new folder for the project under the folder you have specified in the Loca-
tion box. If youwant to put together a short application to test a feature of the language, or perform
some trivial task, you may not wish to save the project. In this case, just clear the check box to skip
the creation of a new project folder.
You can always save a project at any time by choosing the Save All command from the File
menu. You’ll be prompted at that point about the project’s folder, and Visual Studio will save
the project under the folder you specified. If you decide to discard the project, you can create
a new project or close Visual Studio. Visual Studio will prompt you about an open project that
hasn’t been saved yet, and you can choose not to save it.
You may discover at some point that you have created too many projects, which you don’t
really need. You can remove these projects from your system by deleting the corresponding
folders — no special action is required. You’ll know it’s time to remove the unneeded project
folder when Visual Studio suggests project names such as WindowsApplication9 or Windows-
Application49.Petroutsos c01.tex V2 - 01/28/2008 11:43am Page 4
4 CHAPTER 1 GETTING STARTED WITH VISUAL BASIC 2008
Figure 1.2
The New Project dialog
box
For our project, select the Windows Forms Application template; Visual Studio suggests
the name WindowsApplication1 as the project name. Change it to MyTestApplication, select the
Create Directory For Solution check box, and then click the OK button to create the new project.
What you see now is the Visual Studio IDE displaying the Form Designer for a new project, as
shown in Figure 1.3. The main window of your copy of Visual Studio may be slightly different,
but don’t worry about it. I’ll go through all the components you need to access in the process of
designing, coding, and testing a Windows application.
Figure 1.3
The integrated develop-
ment environment of
Visual Studio 2008 for a
new projectPetroutsos c01.tex V2 - 01/28/2008 11:43am Page 5
EXPLORING THE INTEGRATED DEVELOPMENT ENVIRONMENT 5
The new project contains a form already: the Form1 component in the Solution Explorer. The
main window of the IDE is the Form Designer, and the gray surface on it is the window of your
new application in design mode. Using the Form Designer, you’ll be able to design the visible
interface of the application (place various components of the Windows interface on the form and
set their properties) and then program the application.
The default environment is rather crowded, so let’s hide a few of the toolbars that we won’t use
in the projects of the first few chapters. You can always show any of the toolbars at any time. Open
the View menu and choose Toolbars. You’ll see a submenu with 28 commands that are toggles.
Each command corresponds to a toolbar, and you can turn the corresponding toolbar on or off by
clicking one of the commands in the Toolbars submenu. For now, turn off all the toolbars except
for the Layout and Standard toolbars. These are the toolbars shown by default and you shouldn’t
hide them; if you do, this is the place to make them visible again.
The last item in the Toolbars submenu is the Customize command, which leads to a dialog box
in which you can specify which of the toolbars and which of the commands you want to see. After
you have established a work pattern, use this menu to customize the environment for the way you
want to work with Visual Studio. You can hide just about any component of the IDE, except for
the main menu — after all, you have to be able to undo the changes!
Using the Windows Form Designer
To design the form, youmust place on it all the controls you want to display to the user at runtime.
The controls are the components of the Windows interface (buttons, text boxes, radio buttons,
lists, and so on). Open the Toolbox by moving the pointer over the Toolbox tab at the far left; the
Toolbox, shown in Figure 1.4, pulls out. This Toolbox contains an icon for each control you can
use on your form.
The controls are organized into groups according to each control’s function on the interface.
In the first part of the book, we’ll create simple Windows applications and we’ll use the controls
on the Common Controls tab. When you develop web applications, you will see a different set of
icons in the Toolbox.
To place a control on the form, you can double-click the icon of the control. A new instance
with a default size will be placed on the form. Then you can position and resize it with the mouse.
Or you can select the control from the Toolbox with the mouse and then click and drag the
mouse over the form and draw the outline of the control. A new instance of the control will be
placed on the form, and it will fill the rectangle you specified with the mouse. Start by placing a
TextBox control on the form.
The control’s properties will be displayed in the Properties window (see Figure 1.5). This win-
dow, at the far right edge of the IDE and below the Solution Explorer, displays the properties of
the selected control on the form. If the Properties window is not visible, open the View menu and
choose PropertiesWindow, or press F4. If no control is selected, the properties of the selected item
in the Solution Explorer are displayed.
In the Properties window, also known as the Properties Browser, you see the properties that
determine the appearance of the control and (in some cases) its function. The properties are
organized in categories according to their role. The properties that determine the appearance
of the control are listed alphabetically under the header Appearance, the properties that determine
the control’s behavior are listed alphabetically under the header Behavior, and so on. You can
click the AZ button on the window’s title bar to display all properties in alphabetical order.
After you familiarize yourself with the basic properties, you will most likely switch to the
alphabetical list.Petroutsos c01.tex V2 - 01/28/2008 11:43am Page 6
6 CHAPTER 1 GETTING STARTED WITH VISUAL BASIC 2008
Figure 1.4
Windows Forms Toolbox
of the Visual Studio IDE
Rearranging the IDEWindows
As soon as you place a control on the form, the Toolbox retracts to the left edge of the Designer. You
can fix this window on the screen by clicking the icon with the pin on the Toolbox’s toolbar. (It’s the
icon next to the Close icon at the upper-right corner of the Toolbox window, and it appears only when
the Toolbox window is docked, but not while it’s floating.)
You can easily rearrange the various windows that make up the IDE by moving them around with the
mouse. Move the pointer to a window’s title bar, press the left mouse button, and drag the window
around. A window may not follow the mouse, because its position is locked. In this case, click the pin
icon in the upper-right corner of the window to unlock the window’s position and then move it
around with the mouse.
As you move the window, eight semitransparent buttons with arrows appear on the screen, indicat-
ing the area where the window can be docked. Keep moving the window until the pointer hovers overPetroutsos c01.tex V2 - 01/28/2008 11:43am Page 7
EXPLORING THE INTEGRATED DEVELOPMENT ENVIRONMENT 7
one of these buttons, and the docking area appears in semitransparent blue color. Find the desired
docking location for the window and release the mouse. If you release the mouse while the pointer
is not on top of an arrow, the window is not docked. Instead, it remains at the current location as a
floating window, and you can move it around at will with your mouse.
Most developers would rather work with docked windows, and the default positions of the IDE win-
dows are quite convenient. If you want to open even more windows and arrange them differently on
the screen, use the docking feature of the IDE to dock the additional windows.
Locate the TextBox control’s Text property and set it to My TextBox Control by entering the
string into the box next to the property name. The control’s Text property is the string that appears
in the control (the control’s caption), and most controls have a Text property.
Next locate its BackColor property and select it with the mouse. A button with an arrow
appears next to the current setting of the property. Click this button, and you’ll see a dialog box
withthreetabs(Custom,Web,andSystem),asshowninFigure1.6.Inthisdialogbox,youcan
select the color that will fill the control’s background. Set the control’s background color to yellow
and notice that the control’s appearance changes on the form.
One of the settings you’ll want to change is the font of the various controls. While the TextBox
control is still selected on the form, locate the control’s Font property in the Properties window.
You can click the plus sign in front of the property name and set the individual properties of
the font, or you can click the ellipsis button to invoke the Font dialog box. Here you can set the
control’s font and its attributes and then click OK to close the dialog box. Set the TextBox control’s
Font property to Verdana, 14 points, bold. As soon as you close the Font dialog box, the control
on the form is adjusted to the new setting.Petroutsos c01.tex V2 - 01/28/2008 11:43am Page 8
8 CHAPTER 1 GETTING STARTED WITH VISUAL BASIC 2008
Figure 1.5
Properties of a TextBox
control
Figure 1.6
Setting a color prop-
erty in the Properties
windowPetroutsos c01.tex V2 - 01/28/2008 11:43am Page 9
EXPLORING THE INTEGRATED DEVELOPMENT ENVIRONMENT 9
There’s a good chance that the string you assigned to the control’s Text propertywon’t fit in the
control’s width when rendered in the new font. Select the control on the form with the mouse, and
you will see eight handles along its perimeter. Rest the pointer over any of these handles, and it
will assume a shape indicating the direction in which you can resize the control. Make the control
long enough to fit the entire string. If you have to, resize the form as well. Click somewhere on the
form, and when the handles along its perimeter appear, resize it with the mouse.
Some controls, such as the Label, Button, and CheckBox controls, support the AutoSize prop-
erty, which determines whether the control is resized automatically to accommodate its caption.
The TextBox control, as well as many others, doesn’t support the AutoSize property. If you
attempt to make the control tall enough to accommodate a few lines of text, you’ll realize that
you can’t change the control’s height. By default, the TextBox control accepts a single line of text,
and you must set its MultiLine property to True to resize the TextBox control vertically.
The Font Is a Design Element
Like documents, forms should be designed carefully and follow the rules of a printed page design. At
the very least, you shouldn’t use multiple fonts on your forms, just as you shouldn’t mix different
fonts on a printed page. You could use two font families on rare occasions, but you shouldn’t overload
your form. You also shouldn’t use the bold style in excess.
To avoid adjusting the Font property of multiple controls on the form, you should set the form’s font
first, because each control you place on a form inherits the form’s font. If you change the form’s font,
the controls will be adjusted accordingly, but this may throw off the alignment of the controls on the
form. You should experiment with a few Label controls, select a font that you like that’s appropriate
for your interface (you shouldn’t use a handwritten style with a business application, for example)
and then set the form’s Font property to the desired font. Every time you add a new formto the appli-
cation, you should start by setting its Font property to the same font, so that the entire application
will have a consistent look.
The font is the most basic design element, whether you’re designing forms or a document. Various
components of the form may have a different font size, even a different style (like bold or italics),
but there must be a dominant font family that determines the look of the form. The Verdana fam-
ily was designed for viewing documents on computer monitors and is a popular choice. Another great
choice is Segoe UI, a new font family introduced with Windows Vista. The Segoe Print font has a
distinguished handwritten style, and you can use it with graphics applications.
The second most important design element is color, but you shouldn’t get too creative with colors
unless you’re a designer. I recommend that you stay with the default colors and use similar shades
to differentiate a few elements of the interface.
The design of a modern interface has become a new discipline in application development, and there
are tools for designing interfaces. One of themisMicrosoft’s Expression Studio, which enables design-
ers to design the interface and developers to write code, without breaking each other’s work. You can
download a trial version of Expression Studio from www.microsoft.com/expression.
So far, you’ve manipulated properties that determine the appearance of the control. Now you’ll
change a property that determines not only the appearance, but also the function of the control.
Locate the Multiline property. Its current setting is False. Expand the list of available settingsPetroutsos c01.tex V2 - 01/28/2008 11:43am Page 10
10 CHAPTER 1 GETTING STARTED WITH VISUAL BASIC 2008
and change it to True. (You can also change it by double-clicking the name of the property. This
action toggles the True/False settings.) Switch to the form, select the TextBox control, and make it
as tall as you wish.
The Multiline property determines whether the TextBox control can accept one (if Multiline
=False)ormore(if Multiline = True) lines of text. Set this property to True, go back to the Text
property, set it to a long string, and press Enter. The control breaks the long text intomultiple lines.
If you resize the control, the lines will change, but the entire string will fit in the control because the
control’s WordWrap property is True. Set it to False to see how the string will be rendered on
the control.
Multiline TextBox controls usually have a vertical scroll bar so users can quickly locate the
section of text that they’re interested in. Locate the control’s ScrollBars property and expand
the list of possible settings by clicking the button with the arrow. This property’s settings are
None, Vertical, Horizontal,and Both.Setitto Vertical,assignaverylongstringtoits Text
property, and watch how the control handles the text. At design time, you can’t scroll the text on
the control; if you attempt to move the scroll bar, the entire control will be scrolled. The scroll bar
will work as expected at runtime (it will scroll the text vertically).
You can also make the control fill the entire form. Start by deleting all other controls you may
have placed on the form and then select the multiline TextBox. Locate the Dock property in the
Properties window and keep double-clicking the name of the property until its setting changes
to Fill. (You’ll learn a lot more about docking controls in Chapter 7, ‘‘Working with Forms.’’)
The TextBox control fills the form and is resized as you resize the form, both at design time and
runtime.
To examine the control’s behavior at runtime, press F5. The application will be compiled, and
a few moments later, a window filled with a TextBox control will appear on the Desktop (like the
one shown in Figure 1.7). This is what the users of your application would see (if this were an
application worth distributing, of course).
Figure 1.7
ATextBoxcontrol
displaying multiple
text lines
Enter some text on the control, select part of the text, and copy it to the Clipboard by pressing
Ctrl+C. You can also copy text from any other Windows application and paste it on the TextBox
control. Right-click the text on the control and you will see the same context menu you get withPetroutsos c01.tex V2 - 01/28/2008 11:43am Page 11
CREATING YOUR FIRST VB APPLICATION 11
Notepad; you can even change the reading order of the text— not that you’d want to do that with
a Western language. When you’re finished, open the Debug menu and choose Stop Debugging.
This will terminate your application’s execution, and you’ll be returned to the IDE. The Stop
Debugging command is also available as a button with a blue square icon on the toolbar. Finally,
you can stop the running application by clicking the Close button in the application’s window.
The design of a new application starts with the design of the application’s form, which is the
application’s user interface, or UI. The design of the form determines to a large extent the func-
tionality of the application. In effect, the controls on the form determine how the application will
interact with the user. The form itself is a prototype, and you can demonstrate it to a customer
before even adding a single line of code. By placing controls on the form and setting their proper-
ties, you’re implementing a lot of functionality before coding the application. The TextBox control
with the settings discussed in this section is a functional text editor.
Creating Your FirstVBApplication
In this section, we’ll develop a simple application to demonstrate not only the design of the inter-
face, but also the code behind the interface.We’ll build an application that allows the user to enter
the name of his favorite programming language, and the application will evaluate the choice.
Objectively, VB is a step ahead of all other languages, and it will receive the best evaluation. All
other languages get the same grade — good — but not VB.
The project is called WindowsApplication1. You can download the project from the book’s
website and examine it, but I suggest you follow the steps outlined in this section to build the
project from scratch. Start a new project and use the default name, WindowsApplication1,and
place a TextBox and a Button control on the form. Use themouse to position and resize the controls
on the form, as shown in Figure 1.8.
Figure 1.8
A simple applica-
tion that processes a
user-supplied string
Start by setting the form’s Font property to Segoe UI, 9 pt. Arrange and size the controls as
shown in Figure 1.8. Then place a Label control on the form and set its Text property to Enter
your favorite programming language. The Label will be resized according to its caption, becausePetroutsos c01.tex V2 - 01/28/2008 11:43am Page 12
12 CHAPTER 1 GETTING STARTED WITH VISUAL BASIC 2008
the control’s AutoSize property is True. As you move the controls around on the form, you’ll see
some blue lines connecting the edges of the controls when they’re aligned. These lines are called
snap lines, and they allow you to align controls on the form.
Now youmust insert some code to evaluate the user’s favorite language.Windows applications
are made up of small code segments, called event handlers, which react to specific actions such as
the click of a button, the selection of a menu command, the click of a check box, and so on. In the
case of our example, we want to program the action of clicking the button. When the user clicks
the button, we want to execute some code that will display a message.
To insert some code behind the Button control, double-click the control. You’ll see the code
window of the application, which is shown in Figure 1.9. You will see only the definition of
the procedure, not the code that is shown between the two statements in the figure. The line
Private ... is too long to fit on the printed page, so I inserted a line continuation character (an
underscore) to break it into two lines. When a line is too long, you can break it into two (or more)
lines by inserting this character. Alternatively, you can turn on theWordWrap feature of the editor
(you’ll see shortly how to adjust the editor’s properties). Notice that I also inserted quite a bit of
space before the second half of the first code line. It’s customary to indent continued lines so they
can be easily distinguished from the other lines. If you enter the line continuation character in the
editor, the following line will be indented automatically.
Figure 1.9
Outline of a subrou-
tine that handles the
Click event of a Button
control
The editor opens a subroutine, which is delimited by the following statements:
Private Sub Button1 Click(ByVal sender As System.Object,
ByVal e As System.EventArgs) Handles Button1.Click
End Sub
At the top of the main pane of the Designer, you will see two tabs named after the form: the
Form1.vb [Design] tab and the Form1.vb tab. The first tab is the Windows Form Designer (in
which you build the interface of the application with visual tools), and the second is the code
editor (in which you insert the code behind the interface). At the top of the code editor, which is
what you see in Figure 1.9, are two ComboBoxes. The one on the left contains the names of the
controls on the form. The one on the right contains the names of events each control recognizes.
When you select a control (or an object, in general) in the left list, the other list’s contents are
adjusted accordingly. To program a specific event of a specific control, select the name of thePetroutsos c01.tex V2 - 01/28/2008 11:43am Page 13
CREATING YOUR FIRST VB APPLICATION 13
control in the left list (the Objects list) and the name of the event in the right list (the Events list).
While Button1 is selected in the Objects list, open the Events list to see the events to which the
button can react.
The Click event happens to be the default event of the Button control, so when you double-click
a Button on the form, you’re taken to the Button1 Click subroutine. This subroutine is an event
handler, which is invoked automatically every time an event takes place. The event of interest in
our example is the Click event of the Button1 control. Every time the Button1 control on the form
is clicked, the Button1 Click subroutine is activated. To react to the Click event of the button,
you must insert the appropriate code in this subroutine.
There are more than two dozen events for the Button control, and it is among the simpler
controls (after all, what can you do to a button besides clicking it?). Most of the controls recognize
a very large number of events.
The definition of the event handler can’t be modified; this is the event handler’s signature (the
arguments it passes to the application). All event handlers in VB 2008 pass two arguments to
the application: the sender argument, which is an object that represents the control that fired the
event, and the e argument, which provides additional information about the event.
The name of the subroutine is made up of the name of the control, followed by an underscore
and the name of the event. This is just the default name, and you can change it to anything you like
(such as EvaluateLanguage, for this example, or StartCalculations). What makes this subrou-
tine an event handler is the keyword Handles at the end of the statement. The Handles keyword
tells the compiler which event this subroutine is supposed to handle. Button1.Click is the Click
event of the Button1 control. If there were another button on the form, the Button2 control, you’d
have to write code for a subroutine that would handle the Button2.Click event. Each control
recognizes many events, and you can provide a different event handler for each control and event
combination. Of course, we never program every possible event for every control.
The controls have a default behavior and handle the basic events on their own. The TextBox
control knows how to handle keystrokes. The CheckBox control (a small square with a check
mark) changes state by hiding or displaying the check mark every time it’s clicked. The ScrollBar
control moves its indicator (the button in the middle of the control) every time you click one of the
arrows at the two ends. Because of this default behavior of the controls, you need not supply any
code for the events of most controls on the form.
If you change the name of the control after you have inserted some code in an event handler, the
name of the event handled by the subroutine will be automatically changed. The name of the sub-
routine, however, won’t change. If you change the name of the Button1 control to bttnEvaluate,
the subroutine’s header will become
Private Sub Button1 Click(ByVal sender As System.Object,
ByVal e As System.EventArgs) Handles bttnEvaluate.Click
End Sub
Rename the Button1 Click subroutine to EvaluateLanguage. You must edit the code to
change the name of the event handler. I try to name the controls before adding any code to the
application, so that their event handlers will be named correctly. Alternatively, you can use your
own name for each event handler. The default names of the controls you place on a form are quite
generic, and you should change them to something more meaningful. I usually prefix the con-
trol names with a few characters that indicate the control’s type (such as txt, lbl, bttn,andso
on), followed by a meaningful name. Names such as txtLanguage and bttnEvaluate make yourPetroutsos c01.tex V2 - 01/28/2008 11:43am Page 14
14 CHAPTER 1 GETTING STARTED WITH VISUAL BASIC 2008
code far more readable. It’s a good practice to change the default names of the controls as soon
as you add the controls to the form. Names such as Button1, Button2, Button3, and so on, don’t
promote the readability of your code. With the exception of this first sample project, I’m using
more-meaningful names for the controls used in this book’s projects.
Let’s add some code to the Click event handler of the Button1 control. When this button is
clicked, we want to examine the text in the text box. If it’s Visual Basic,wedisplayamessage;if
not, we display a different message. Insert the lines of Listing 1.1 between the Private Sub and
End Sub statements. (I’m showing the entire listing here; there’s no reason to retype the first and
last statements.)
Listing 1.1: Processing a User-Supplied String
Private Sub EvaluateLanguage(ByVal sender As System.Object,
ByVal e As System.EventArgs) Handles Button1.Click
Dim language As String
language = TextBox1.Text
If language = ”Visual Basic” Then
MsgBox(”We have a winner!”)
Else
MsgBox(language & ”is not a bad language.”)
End If
End Sub
Here’s what this code does. First, it assigns the text of the TextBox control to the variable lan-
guage.A variable is a named location in memory where a value is stored. Variables are where we
store the intermediate results of our calculations when we write code. All variables are declared
with a Dim statement and have a name and a type.
You could also declare and assign a value to the language variable in a single step:
Dim language = TextBox1.Text
The compiler will create a String variable, because the statement assigns a string to the variable.
We’ll come back to the topic of declaring and initializing variables in Chapter 2, ‘‘Variables and
Data Types.’’
Then the program compares the value of the language variable to the literal Visual Basic,and
depending on the outcome of the comparison, it displays one of two messages. The MsgBox()
function displays the specified message in a small window with the OK button, as shown in
Figure 1.8. Users can view the message and then click the OK button to close the message box.
Even if you’re not familiar with the syntax of the language, you should be able to understand
what this code does. Visual Basic is the simplest of the languages supported by Visual Studio 2008,
and we will discuss the various aspects of the language in detail in the following chapters. In the
meantime, you should try to understand the process of developing a Windows application: how
to build the visible interface of the application and how to program the events to which you want
your application to react.
The code of our first application isn’t very robust. If the user doesn’t enter the string with the
exact spelling shown in the listing, the comparison will fail.We can convert the string to uppercasePetroutsos c01.tex V2 - 01/28/2008 11:43am Page 15
CREATING YOUR FIRST VB APPLICATION 15
and then compare it with VISUAL BASIC to eliminate differences in case. To convert a string to
uppercase, use the ToUpper method of the String class. The following expression returns the string
stored in the language variable, converted to uppercase:
language.ToUpper
We should also take into consideration the fact that the user may enter VB or VB 2008,andso
on. In the following section, we’ll further improve our application. You never know what users
may throw at your application, so whenever possible you should try to limit their responses to the
number of available choices. In our case, we can display the names of certain languages (the ones
we’re interested in) and force the user to select one of them.
One way to display a limited number of choices is to use a ComboBox control. In the following
section, we’ll revise our sample application so that users won’t have to enter the name of the
language. We’ll force them to select their favorite language from a list so that we won’t have to
validate the string supplied by the user.
Making the Application More User-Friendly
Start a new project: theWindowsApplication2 project. Do not select the Create Directory For Solu-
tion check box; we’ll save the project from within the IDE. As soon as the project is created, open
the File menu and choose Save All to save the project. When the Save Project dialog box appears,
click the Browse button to select the folder where the project will be saved. In the Project Location
dialog box that appears, select an existing folder or create a new folder such as MyProjects or
VB.NET Samples.
Open the Toolbox and double-click the icon of the ComboBox tool. A ComboBox control will
be placed on your form. Now place a Button control on the form and position it so that your
form looks like the one shown in Figure 1.10. Then set the button’s Text property to Evaluate
My Choice.
Figure 1.10
Displaying options in a
ComboBox control
We must now populate the ComboBox control with the valid choices. Select the ComboBox
control on the form by clicking it with the mouse and locate its Items property in the Properties
window. The setting of this property is Collection, which means that the Items property doesn’t
have a single value; it’s a collection of items (strings, in this case). Click the ellipsis button and
you’ll see the String Collection Editor dialog box, as shown in Figure 1.11.Petroutsos c01.tex V2 - 01/28/2008 11:43am Page 16
16 CHAPTER 1 GETTING STARTED WITH VISUAL BASIC 2008
Figure 1.11
Click the ellipsis button
next to the Items prop-
erty of a ComboBox to
see the String Collection
Editor dialog box.
The main pane in the String Collection Editor dialog box is a TextBox, in which you can enter
the items you want to appear in the ComboBox control at runtime. Enter the following strings, one
per row and in the order shown here:
C++
C#
Visual Basic
Java
Cobol
Click the OK button to close the dialog box. The items will not appear on the control at design
time, but you will see them when you run the project. Before running the project, set one more
property. Locate the ComboBox control’s Text property and set it to Select your favorite pro-
gramming language. This is not an item of the list; it’s the string that will initially appear on the
control.
You can run the project now and see how the ComboBox control behaves. Press F5 and wait
a few seconds. The project will be compiled, and you’ll see its form on your Desktop, on top of
the Visual Studio window. I’m sure you know how the ComboBox control behaves in a typical
Windows application, and our sample application is no exception. You can select an item on the
control, either with the mouse or with the keyboard. Click the button with the arrow to expand the
list and then select an item with the mouse. Or press the down or up arrow keys to scroll through
the list of items. The control isn’t expanded, but each time you click an arrow button, the next or
previous item in the list appears on the control. Press the Tab key to move the focus to the Button
control and press the spacebar to emulate a Click event (or simply click the Button control).
We haven’t told the application what to do when the button is clicked, so let’s go back and
add some code to the project. Stop the application by clicking the Stop button on the toolbar (the
solid black square) or by choosing Debug  Stop Debugging from the main menu.When the form
appears in design mode, double-click the button, and the code window will open, displaying an
empty Click event handler. Insert the statements shown in Listing 1.2 between the Private Sub
and End Sub statements.Petroutsos c01.tex V2 - 01/28/2008 11:43am Page 17
CREATING YOUR FIRST VB APPLICATION 17
Listing 1.2: The Revised Click Event Handler
Private Sub Button1 Click(ByVal sender As System.Object,
ByVal e As System.EventArgs) Handles Button1.Click
Dim language As String
language = ComboBox1.Text
If language = ”Visual Basic” Then
MsgBox(”We have a winner!”)
Else
MsgBox(language & ”is not a bad language.”)
End If
End Sub
When the form is first displayed, a string that doesn’t correspond to a language is displayed
in the ComboBox control. We can preselect one of the items from within our code when the form
is first loaded. When a form is loaded, the Load event of the Form object is raised. Double-click
somewhere on the form and the editor will open the form’s Load event handler:
Private Sub Form1 Load(ByVal sender As System.Object,
ByVal e As System.EventArgs) Handles MyBase.Load
End Sub
Enter the following code to select the item Visual Basic when the form is loaded:
Private Sub Form1 Load(ByVal sender As System.Object,
ByVal e As System.EventArgs) Handles MyBase.Load
ComboBox1.SelectedIndex = 2
End Sub
SelectedIndex is a property of the ComboBox control that determines the selected item. You
can set it to an integer value from within your code to select an item on the control, and you can
also use it to retrieve the index of the selected item in the list. Instead of comparing strings, we
can compare the SelectedIndex property to the value that corresponds to the index of the item
Visual Basic, with a statement such as the following:
If ComboBox1.SelectedIndex = 2 Then
MsgBox(”We have a winner!”)
Else
MsgBox(ComboBox1.Text & ”is not a bad language.”)
End If
The Text property of the ComboBox control returns the text on the control, and we use it to
print the selected language’s name. Of course, if you insert or remove items from the list, you
must edit the code accordingly. If you run the application and test it thoroughly, you’ll realizePetroutsos c01.tex V2 - 01/28/2008 11:43am Page 18
18 CHAPTER 1 GETTING STARTED WITH VISUAL BASIC 2008
that there’s a problem with the ComboBox control. Users can type a new string in the control,
which will be interpreted as a language. By default, the ComboBox control allows users to type in
something, in addition to selecting an item from the list. To change the control’s behavior, select
it on the form and locate its DisplayStyle property in the Properties window. Expand the list of
possible settings for the control and change the property’s value from DropDown to DropDown-
List. Run the application again and test it; our sample application has become bulletproof. It’s a
simple application, but you’ll see more techniques for building robust applications in Chapter 4,
‘‘GUI Design and Event-Driven Programming.’’
The controls on the Toolbox are more than nice pictures we place on our forms. They encapsu-
late a lot of functionality and expose properties that allow us to adjust their appearance and their
functionality. Most properties are usually set at design time, but quite frequently we change the
properties of various controls from within our code.
Now that you’re somewhat familiar with the process of building Windows applications, and
before you look into any additional examples, I will quickly present the components of the Visual
Studio IDE.
Understanding the IDE Components
The IDE of Visual Studio 2008 contains numerous components, and it will take you a while to
explore them. It’s practically impossible to explain in a single chapter what each tool, window, and
menu command does. We’ll discuss specific tools as we go along and as the topics get more
and more advanced. In this section, I will go through the basic items of the IDE — the ones we’ll
use in the following few chapters to build simple Windows applications.
The IDE Menu
The IDE menu provides the following commands, which lead to submenus. Notice that most
menus can also be displayed as toolbars. Also, not all options are available at all times. The options
that cannot possibly apply to the current state of the IDE are either invisible or disabled. The Edit
menu is a typical example. It’s quite short when you’re designing the formand quite lengthy when
you edit code. The Data menu disappears altogether when you switch to the code editor — you
can’t use the options of this menu while editing code. If you open an XML document in the IDE,
the XML command will be added to the main menu of Visual Studio.
File Menu
The File menu contains commands for opening and saving projects or project items, as well as
commands for adding new or existing items to the current project. For the time being, use the
New  Project command to create a new project, Open  Project/Solution to open an existing
project or solution, Save All to save all components of the current project, and the Recent Projects
submenu to open one of the recent projects.
Edit Menu
The Edit menu contains the usual editing commands. Among these commands are the Advanced
command and the IntelliSense command. Both commands lead to submenus, which are discussed
next. Note that these two items are visible only when you’re editing your code, and are invisible
while you’re designing a form.Petroutsos c01.tex V2 - 01/28/2008 11:43am Page 19
UNDERSTANDING THE IDE COMPONENTS 19
Edit  Advanced Submenu
The more-interesting options of the Edit  Advanced submenu are the following:
View White Space Space characters (necessary to indent lines of code and make it easy to
read) are replaced by periods.
Word Wrap When a code line’s length exceeds the length of the code window, the line is
automatically wrapped.
Comment Selection/Uncomment Selection Comments are lines you insert between your
code’s statements to document your application. Every line that begins with a single quote
is a comment; it is part of the code, but the compiler ignores it. Sometimes, we want to dis-
ableafewlinesfromourcodebutnotdeletethem(becausewewanttobeabletorestore
them later). A simple technique to disable a line of code is to comment it out (insert the com-
ment symbol in front of the line). This command allows you to comment (or uncomment)
largesegmentsofcodeinasinglemove.
Edit  IntelliSense Submenu
The Edit  IntelliSense menu item leads to a submenu with five options, which are described
next. IntelliSense is a feature of the editor (and of other Microsoft applications) that displays as
much information as possible, whenever possible. When you type the name of a control and the
following period, IntelliSense displays a list of the control’s properties and methods, so that you
can select the desired one, rather than guessing its name. When you type the name of a function
and the opening parenthesis, IntelliSense will display the syntax of the function — its arguments.
The IntelliSense submenu includes the following options:
List Members When this option is on, the editor lists all the members (properties, methods,
events, and argument list) in a drop-down list. This list will appear when you enter the name of
an object or control followed by a period. Then you can select the desired member from the list
with the mouse or with the keyboard. Let’s say your form contains a control named TextBox1
and you’re writing code for this form. When you enter the name of the control followed by
aperiod(TextBox1.), a list with the members of the TextBox control will appear (as seen in
Figure 1.12).
In addition, a description of the selected member is displayed in a ToolTip box, as you can
see in the same figure. Select the Text property and then enter the equal sign, followed by a
string in quotes, as follows:
TextBox1.Text = ”Your User Name”
If you select a property that can accept a limited number of settings, you will see the names
of the appropriate constants in a drop-down list. If you enter the following statement, you will
see the constants you can assign to the property (see Figure 1.13):
TextBox1.TextAlign =
Again, you can select the desired value with the mouse. The drop-down list with the members
of a control or object (the Members list) remains open until you type a terminator key (the Esc
or End key) or select a member by pressing the space bar or the Enter key.Petroutsos c01.tex V2 - 01/28/2008 11:43am Page 20
20 CHAPTER 1 GETTING STARTED WITH VISUAL BASIC 2008
Figure 1.12
Viewing the members
of a control in the
IntelliSense
drop-down list
Figure 1.13
Viewing the possible
settings of a prop-
erty in the IntelliSense
drop-down listPetroutsos c01.tex V2 - 01/28/2008 11:43am Page 21
UNDERSTANDING THE IDE COMPONENTS 21
Parameter Info While editing code, you can move the pointer over a variable, method, or
property and see its declaration in a yellow pop-up box. You can also jump to the variable’s
definition or the body of a procedure by choosing Go To Definition from the context menu that
will appear if you right-click the variable or method name in the code window.
Quick Info This is another IntelliSense feature that displays information about commands
and functions. When you type the opening parenthesis following the name of a function, for
example, the function’s arguments will be displayed in a ToolTip box (a yellow horizontal box).
The first argument appears in bold font; after entering a value for this argument, the next one is
shown in bold. If an argument accepts a fixed number of settings, these values will appear in a
drop-down list, as explained previously.
Complete Word The Complete Word feature enables you to complete the current word by
pressing Ctrl+spacebar. For example, if you type TextB and then press Ctrl+spacebar, you
will see a list of words that you’re most likely to type (TextBox, TextBox1, and so on).
Insert Snippet This command opens the Insert Snippet window at the current location in the
code editor window. Code snippets, which are an interesting feature of Visual Studio 2008, are
discussed in the section ‘‘Using Code Snippets’’ later in this chapter.
Edit  Outlining Submenu
A practical application contains a substantial amount of code in a large number of event han-
dlers and custom procedures (subroutines and functions). To simplify the management of the
code window, the Outlining submenu contains commands that collapse and expand the various
procedures.
Let’s say you’re finished editing the Click event handlers of several buttons on the form. You
can reduce these event handlers to a single line that shows the names of the procedures and a
plus sign in front of them. You can expand a procedure’s listing at any time by clicking the plus
sign in front of its name. When you do so, a minus sign appears in front of the procedure’s name,
and you can click it to collapse the body of the procedure again. The Outlining submenu contains
commands to handle the outlining of the various procedures, or turn off outlining and view the
complete listings of all procedures. You will use these commands as you write applications with
substantial amounts of code:
Toggle Outlining Expansion This option lets you change the outline mode of the current
procedure. If the procedure’s definition is collapsed, the code is expanded, and vice versa.
Toggle All Outlining This option is similar to the Toggle Outlining Expansion option, but
it toggles the outline mode of the current document. A form is reduced to a single statement.
A file with multiple classes is reduced to one line per class.
Stop Outlining This option turns off outlining and adds a new command to the
Outlining submenu, Start Automatic Outlining, which you can select to turn on automatic
outlining again.
Collapse To Definitions This option reduces the listing to a list of procedure headers.
View Menu
This menu contains commands to display any toolbar or window of the IDE. You have already
seen the Toolbars menu (in the ‘‘Starting a New Project’’ section). The Other Windows command
leads to a submenu with the names of some standard windows, including the Output and Com-
mand windows. The Output window is the console of the application. The compiler’s messages,Petroutsos c01.tex V2 - 01/28/2008 11:43am Page 22
22 CHAPTER 1 GETTING STARTED WITH VISUAL BASIC 2008
for example, are displayed in the Output window. The Command window allows you to enter
and execute statements.When you debug an application, you can stop it and enter VB statements
in the Command window.
Project Menu
Thismenu contains commands for adding items to the current project (an itemcan be a form, a file,
a component, or even another project). The last option in this menu is the Project Properties com-
mand, which opens the project’s Properties Pages. The Add Reference and Add Web Reference
commands allow you to add references to .NET components and web components, respectively.
Build Menu
The Build menu contains commands for building (compiling) your project. The two basic com-
mands in this menu are Build and Rebuild All. The Build command compiles (builds the exe-
cutable) of the entire solution, but it doesn’t compile any components of the project that haven’t
changed since the last build. The Rebuild All command does the same, but it clears any existing
files and builds the solution from scratch.
Debug Menu
This menu contains commands to start or end an application,aswellasthebasicdebuggingtools.
The basic commands of this menu are discussed briefly in Chapter 4 and in Appendix B.
Data Menu
This menu contains commands you will use with projects that access data. You’ll see how to
use this short menu’s commands in the discussion of the visual database tools in Chapters 21 and
22 of the book.
Format Menu
The Format menu, which is visible only while you design a Windows or web form, contains
commands for aligning the controls on the form. The commands of this menu are discussed in
Chapter 4. The Format menu is invisible when you work in the code editor — its commands apply
to the visible elements of the interface.
Tools Menu
This menu contains a list of useful tools, such as the Macros command, which leads to a submenu
with commands for creating macros. Just as you can create macros in a Microsoft Office applica-
tion to simplify many tasks, you can create macros to automate many of the repetitive tasks you
perform in the IDE. The last command in this menu, the Options command, leads to the Options
dialog box, in which you can fully customize the environment. The Choose Toolbox Items com-
mand opens a dialog box that enables you to add more controls to the Toolbox. In Chapter 12,
‘‘Building Custom Windows Controls,’’ you’ll learn how to design custom controls and add them
to the Toolbox.
Window Menu
This is the typical Window menu of any Windows application. In addition to the list of open
windows, it also contains the Hide command, which hides all toolboxes, leaving the entirePetroutsos c01.tex V2 - 01/28/2008 11:43am Page 23
UNDERSTANDING THE IDE COMPONENTS 23
window of the IDE devoted to the code editor or the Form Designer. The toolboxes don’t dis-
appear completely; they’re all retracted, and you can see their tabs on the left and right edges of
the IDE window. To expand a toolbox, just hover the mouse pointer over the corresponding tab.
Help Menu
This menu contains the various help options. The Dynamic Help command opens the Dynamic
Help window, which is populated with topics that apply to the current operation. The Index
command opens the Index window, in which you can enter a topic and get help on the specific
topic.
Toolbox Window
The Toolbox window contains all the controls you can use to build your application’s interface.
This window is usually retracted, and you must move the pointer over it to view the Toolbox. The
controls in the Toolbox are organized in various tabs, so take a look at them to become familiar
with the controls and their functions.
In the first few chapters, we’ll work with the controls in the Common Controls and Menus &
Toolbars tabs. The Common Controls tab contains the icons of the most common Windows con-
trols. The Data tab contains the icons of the objects you will use to build data-driven applications
(they’re explored later in this book). The Dialogs tab contains controls for implementing the com-
mon dialog controls, which are so common inWindows interfaces; they’re discussed in Chapter 8,
‘‘MoreWindows Controls.’’
Solution Explorer Window
The Solution Explorer window contains a list of the items in the current solution. A solution can
contain multiple projects, and each project can contain multiple items. The Solution Explorer
displays a hierarchical list of all the components, organized by project. You can right-click any
component of the project and choose Properties in the context menu to see the selected com-
ponent’s properties in the Properties window. If you select a project, you will see the Project
Properties dialog box. You will find more information on project properties in the following
chapter.
If the solution contains multiple projects, you can right-click the project you want to become
the startup form and select Set As StartUp Project. You can also add items to a project with the
Add Item command of the context menu, or remove a component from the project with the
Exclude From Project command. This command removes the selected component from the project,
but doesn’t affect the component’s file on the disk. The Delete command removes the selected
component from the project and also deletes the component’s file from the disk.
Properties Window
This window (also known as the Properties Browser) displays all the properties of the selected
component and its settings. Every time you place a control on a form, you switch to this window
to adjust the appearance of the control. You have already seen how to manipulate the properties
of a control through the Properties window.
Many properties are set to a single value, such as a number or a string. If the possible settings
of a property are relatively few, they’re displayed as meaningful constants in a drop-down list.
Other properties are set through a more elaborate interface. Color properties, for example, are set
from within a Color dialog box that’s displayed right in the Properties window. Font properties
are set through the usual Font dialog box. Collections are set in a Collection Editor dialog box,Petroutsos c01.tex V2 - 01/28/2008 11:43am Page 24
24 CHAPTER 1 GETTING STARTED WITH VISUAL BASIC 2008
in which you can enter one string for each item of the collection, as you did for the items of the
ComboBox control earlier in this chapter.
If the Properties window is hidden, or if you have closed it, you can either choose View 
Properties Window, or right-click a control on the form and choose Properties. Or you can simply
press F4 to bring up this window. There will be times when a controlmight totally overlap another
control, and you won’t be able to select the hidden control and view its properties. In this case,
you can select the desired control in the ComboBox at the top of the Properties window. This box
contains the names of all the controls on the form, and you can select a control on the form by
selecting its name on this box.
Output Window
The Output window is where many of the tools, including the compiler, send their output. Every
time you start an application, a series of messages is displayed in the Output window. These
messages are generated by the compiler, and you need not understand them at this point. If the
Output window is not visible, choose View  Other Windows  Output from the menu.
Command and Immediate Windows
While testing a program, you can interrupt its execution by inserting a so-called breakpoint.When
the breakpoint is reached, the program’s execution is suspended, and you can execute a statement
in the Immediate window. Any statement that can appear in your VB code can also be executed in
the Immediate window. To evaluate an expression, enter a question mark followed by the expres-
sion you want to evaluate, as in the following samples, where result is a variable in the program
you interrupted:
? Math.Log(35)
? ”The answer is ” & result.ToString
You can also send output to this window from within your code with the Debug.Write and
Debug.WriteLine methods. Actually, this is a widely used debugging technique — to print the
values of certain variables before entering a problematic area of the code. There are more elaborate
tools to help you debug your application, and you’ll find a discussion in Appendix B, but printing
a few values to the Immediate window is a time-honored practice in programming with VB.
In many of the examples of this book, especially in the first few chapters, I use the Debug.
WriteLine statement to print something to the Immediate window. To demonstrate the use of the
DateDiff() function, for example, I’ll use a statement like the following:
Debug.WriteLine(DateDiff(DateInterval.Day, #3/9/2007#, #5/15/2008#))
When this statement is executed, the value 433 will appear in the Immediate window. This
statement demonstrates the syntax of the DateDiff() function, which returns the difference
between the two dates in days. Sending some output to the Immediate window to test a function
or display the results of intermediate calculations is a common practice.
To get an idea of the functionality of the Immediate window, switch back to your first sample
application and insert the Stop statement after the End If statement in the button’s Click event
handler. Run the application, select a language, and click the button on the form. After displaying
a message box, the application will reach the Stop statement and its execution will be suspended.Petroutsos c01.tex V2 - 01/28/2008 11:43am Page 25
SETTING ENVIRONMENT OPTIONS 25
You’ll see the Immediate window at the bottomof the IDE. If it’s not visible, open the Debugmenu
and choose Windows  Immediate. In the Immediate window, enter the following statement:
? ComboBox1.Items.Count
Then press Enter to execute it. Notice that IntelliSense is present while you’re typing in the Imme-
diate window. The expression prints the number of items in the ComboBox control. (Don’t worry
about the numerous properties of the control and the way I present them here; they’re discussed
in detail in Chapter 6, ‘‘Basic Windows Controls.’’) As soon as you press Enter, the value 5 will be
printed on the following line.
You can also manipulate the controls on the form from within the Immediate window. Enter
the following statement and press Enter to execute it:
ComboBox1.SelectedIndex = 4
The fifth item on the control will be selected (the indexing of the items begins with 0). How-
ever, you can’t see the effects of your changes, because the application isn’t running. Press F5 to
resume the execution of the application and you will see that the item Cobol is now selected in the
ComboBox control.
The Immediate window is available only while the application’s execution is suspended. To
continue experimenting with it, click the button on the form to evaluate your choice. When the
Stop statement is executed again, you’ll be switched to the Immediate window.
Unlike the Immediate window, the Command window is available at design time. The Com-
mand window allows you to access all the commands of Visual Studio by typing their names in
this window. If you enter the string Edit followed by a period, you will see a list of all commands
of the Edit menu, including the ones that are not visible at the time, and you can invoke any of
these commands and pass arguments to them. For example, if you enter Edit.Find ”Margin” in
the Command window and then press Enter, the first instance of the string Margin will be located
in the open code window. To start the application, you can type Debug.Start.Youcanaddanew
project to the current solution with the AddProj command, and so on. Most developers hardly
ever use this window in designing or debugging applications.
Error List Window
This window is populated by the compiler with error messages, if the code can’t be successfully
compiled. You can double-click an error message in this window, and the IDE will take you to the
line with the statement in error — which you should fix. Change the MsgBox() function name to
MssgBox(). As soon as you leave the line with the error, the name of the function will be under-
lined with a wiggly red line and the following error description will appear in the Error List
window:
Name ’MssgBox’ is not declared
Setting EnvironmentOptions
The Visual Studio IDE is highly customizable. I will not discuss all the customization options here,
but I will show you how to change the default settings of the IDE. Open the Tools menu and select
Options (the last item in the menu). The Options dialog box appears, in which you can set all thePetroutsos c01.tex V2 - 01/28/2008 11:43am Page 26
26 CHAPTER 1 GETTING STARTED WITH VISUAL BASIC 2008
options regarding the environment. Figure 1.14 shows the options for the fonts of the various
items of the IDE. Here you can set the font for the Text Editor, dialog boxes, toolboxes, and so on.
Select an item in the tree in the left pane list and then set the font for this item in the box below.
Figure 1.14
The Fonts And Colors
options
Figure 1.15 shows the Projects And Solutions options. The top box indicates the default location
for new projects. The Save New Projects When Created check box determines whether the editor
will create a new folder for the project when it’s created. If you uncheck this box, then Visual
Studio will create a folder in the Temp folder. Projects in the Temp folder will be removed when
you run the Disk Cleanup utility to claim more space on your hard drives.
Figure 1.15
The Projects And Solu-
tions options
By default, Visual Studio saves the changes to the current project every time you press F5. You
can change this behavior by setting the Before Building option in the Build And Run page, under
the Project And Solutions branch. If you change this setting, you must save your project from time
to time with the File  Save All command.
Most of the tabs in the Options dialog box are straightforward, and you should take a look at
them. If you don’t like some of the default aspects of the IDE, this is the place to change them. IfPetroutsos c01.tex V2 - 01/28/2008 11:43am Page 27
BUILDING A CONSOLE APPLICATION 27
you switch to the Basic item under the Text Editor branch of the tree in the left pane of the Options
dialog box, you will find the Line Numbers option. Select this check box to display numbers
in front of each line in the code window. The Options dialog box contains a lot of options for
customizing your work environment, and it’s worth exploring on your own.
Building a ConsoleApplication
Apart from Windows applications, you can use Visual Studio 2008 to build applications that run
in a command prompt window. The command prompt window isn’t really a DOS window, even
though it looks like one. It’s a text window, and the only way to interact with an application is to
enter lines of text and read the output generated by the application, which is displayed in this text
window, one line at a time. This type of application is called a console application, and I’m going
to demonstrate console applications with a single example. We will not return to this type of
application later in the book because it’s not what you’re supposed to do as aWindows developer.
The console application you’ll build in this section, ConsoleApplication1, prompts the user to
enter the name of her favorite language. It then prints the appropriate message on a new line, as
shown in Figure 1.16.
Figure 1.16
A console application
uses the command
prompt window to inter-
act with the user.
Start a new project. In the New Project dialog box, select the template Console Application. You
can also change its default name from ConsoleApplication1 to a more descriptive name. For this
example, don’t change the application’s name.
A console application doesn’t have a user interface, so the first thing you’ll see is the code
editor’s window with the following statements:
Module Module1
Sub Main()
End Sub
End Module
Unlike a Windows application, which is a class, a console application is a module. Main() is
the name of a subroutine that’s executed automatically when you run a console application. The
code you want to execute must be placed between the statements Sub Main() and End Sub.Insert
the statements shown in Listing 1.3 in the application’s Main() subroutine.Petroutsos c01.tex V2 - 01/28/2008 11:43am Page 28
28 CHAPTER 1 GETTING STARTED WITH VISUAL BASIC 2008
Listing 1.3: Console Application
Module Module1
Sub Main()
Console.WriteLine(”Enter your favorite language”)
Dim language As String
language = Console.ReadLine()
language = language.ToUpper
If language = ”VISUAL BASIC” Or
language = ”VB” Or
language = ”VB.NET” Then
Console.WriteLine(”We have a winner!”)
Else
Console.WriteLine(language & ”is not a bad language.”)
End If
Console.WriteLine()
Console.WriteLine()
Console.WriteLine(”PRESS ENTER TO EXIT”)
Console.ReadLine()
End Sub
End Module
This code is quite similar to the code of the equivalent Windows applications we developed
earlier, except that it uses the Console.WriteLine statement to send its output to the command
prompt window instead of a message box.
A console application doesn’t react to events because it has no visible interface. However, it’s
easy to add some basic elements of the Windows interface to a console application. If you change
the Console.WriteLine method call into the MsgBox() function, the message will be displayed in
a message box.
The reason to build a console application is to test a specific feature of the language without
having to build a user interface. Many of the examples in the documentation are console applica-
tions; they demonstrate the topic at hand and nothing more. If you want to test the DateDiff()
function, for example, you can create a new console application and enter the lines of Listing 1.4
in its Main() subroutine.
Listing 1.4: Testing the DateDiff() Function with a Console Application
Sub Main()
Console.WriteLine(DateDiff(DateInterval.Day, #3/9/2000#, #5/15/2008#))
Console.WriteLine(”PRESS ENTER TO EXIT”)
Console.ReadLine()
End Sub
The last two lines will be the same in every console application you write. Without them, the
command prompt window will close as soon as the End Sub statement is reached, and you won’tPetroutsos c01.tex V2 - 01/28/2008 11:43am Page 29
USING CODE SNIPPETS 29
have a chance to see the result. The Console.ReadLine method waits until the user presses the
Enter key.
Console applications are convenient for testing short code segments, but Windows program-
ming is synonymous with designing graphical user interfaces, so you won’t find any more console
applications in this book.
Using Code Snippets
Visual Basic 2008 comes with a lot of predefined code snippets for selected actions, and you can
insert these snippets in your code as needed. Let’s say you want to insert the statements for writing
some text to a file, but you have no idea how to access files. Create an empty line in the listing
(press the Enter key a couple of times at the end of a code line). Then open the Edit menu and
choose IntelliSense  Insert Snippet (or right-click somewhere in the code window and choose
Insert Snippet from the context menu).
You will see on the screen a list of the snippets, organized in folders according to their function,
as shown in Figure 1.17. Select the fundamentals folder, which will display another list of options:
collections and arrays, datatypes, filesystem,and math. Double-click the filesystem item to see a list of
common file-related tasks, as shown in Figure 1.18. Locate the item Write Text To A File in the list
and double-click it to insert the appropriate snippet at the current location in the code window.
Figure 1.17
The code snippets orga-
nized according to their
function
The following snippet will be inserted in your code:
My.Computer.FileSystem.WriteAllText(”C:\test.txt”, ”Text”, True)
To write some text to a file, you need to call the WriteAllText method of the My.Computer
.FileSystem object. You can replace the strings shown in the snippet with actual values. The first
string is the filename, the second string is the text to be written to the file, and the last argument
of the method determines whether the text will be appended to the file (if False) or will overwrite
any existing text (if True).
The snippet shows you the basic statements for performing a common task, and you can edit
the code inserted by Visual Studio as needed. A real-world application would probably promptPetroutsos c01.tex V2 - 01/28/2008 11:43am Page 30
30 CHAPTER 1 GETTING STARTED WITH VISUAL BASIC 2008
the user for a filename via the File common dialog box and then use the filename specified by the
user in the dialog box, instead of a hard-coded file name.
Figure 1.18
Selecting a code snippet
to insert in your code
As you program, you should always try to find out whether there’s a snippet for the task at
hand. Sometimes you can use a snippet without even knowing how it works. Although snippets
can simplify your life, they won’t help you understand the Framework, which is discussed in
detail throughout this book.
Using theMyObject
You have probably noticed that the code snippets of Visual Studio use an entity called My, which
is a peculiar object that was introduced with VB 2005 to simplify many programming tasks. As
you saw in the preceding code snippet, the My object allows you to write some text to a file with
a single statement, the WriteAllText method. If you’re familiar with earlier versions of Visual
Basic, you know that you must first open a file, and then write some text to it, and finally close the
file. The My object allows you to perform all these operations with a single statement, as you saw
in the preceding example.
Another example is the Play method, which you can use to play back a WAV file from within
your code:
My.Computer.Audio.Play (”C:\Sounds\CountDown.wav”)
You can also use the following expression to play back a system sound:
My.Computer.Audio.PlaySystemSound(System.Media.SystemSounds.Exclamation)Petroutsos c01.tex V2 - 01/28/2008 11:43am Page 31
USING THE MY OBJECT 31
The method that plays back the sound is the Play method, and the method that writes text to
a file is the WriteAllText method. However, you can’t call them directly through the My object;
they’re not methods of the My object. If they were, you’d have to dig hard to find out the method
you need. The My object exposes six components, which contain their own components. Here’s a
description of the basic components of the My object and the functionality you should expect to
find in each component:
My.Application The Application component provides information about the current appli-
cation. The CommandLineArgs property of My.Application returns a collection of strings, which
are the arguments passed to the application when it was started. TypicalWindows applications
aren’t called with command-line arguments, but it’s possible to start an application and pass a
filename as an argument to the application (the document to be opened by the application, for
example). The Info property is an object that exposes properties such as DirectoryPath (the
application’s default folder), ProductName, Version,andsoon.
Computer This component of the My object exposes a lot of functionality via a number of
properties, many of which are objects. The My.Computer.Audio component lets you play back
sounds. The My.Computer.Clipboard component lets you access the Clipboard. To find out
whether the Clipboard contains a specific type of data, use the ContainsText, ContainsImage,
ContainsData,and ContainsAudio methods. To retrieve the contents of the Clipboard, use
the GetText, GetImage, GetData,and GetAudioStream methods. Assuming that you have
a form with a TextBox control and a PictureBox control, you can retrieve text or image data
from the Clipboard and display it on the appropriate control with the following statements:
If My.Computer.Clipboard.ContainsImage Then
PictureBox1.Image = My.Computer.Clipboard.GetImage
End If
If My.Computer.Clipboard.ContainsText Then
TextBox2.Text = My.Computer.Clipboard.GetText
End If
You may have noticed that using the My object in your code requires that you write long
statements. You can shorten them substantially via the With statement, as shown next:
With My.Computer.Clipboard
If .ContainsImage Then
PictureBox1.Image = .GetImage
End If
If .ContainsText Then
TextBox2.Text = .GetText
End If
End With
When you’re executing multiple statements on the same object, you can specify the object in
a With statement and call its methods in the block of the With statement by specifying the
method name prefixed with a period. The With statement is followed by the name of the object
to which all following methods apply, and is terminated with the End With statement.Petroutsos c01.tex V2 - 01/28/2008 11:43am Page 32
32 CHAPTER 1 GETTING STARTED WITH VISUAL BASIC 2008
Another property of the My.Computer component is the FileSystem object that exposes all
the methods you need to access files and folders. If you enter the expression My.Computer
.FileSystem followed by a period, you will see all the methods exposed by the FileSystem
component. Among them, you will find DeleteFile, DeleteDirectory, RenameFile,
RenameDirectory, WriteAllText, ReadAllText, and many more. Select a method and then
type the opening parenthesis. You will see the syntax of the method in a ToolTip. The syntax of
the CopyFile method is as follows:
My.Computer.FileSystem.CopyFile(
sourceFileName As String, destinationFileName As String)
Just specify the path of the file you want to copy and the new file’s name, and you’re finished.
This statement will copy the specified file to the specified location.
You will notice that the ToolTip box with the syntax of the CopyFile method has multiple ver-
sions, which are listed at the left side of the box along with arrow up and arrow down icons.
Click these two buttons to see the next and previous versions of the method. The second ver-
sion of the CopyFile method is as follows:
My.Computer.FileSystem.CopyFile(
sourceFileName As String, destinationFileName As String,
overwrite As Boolean)
The overwrite argument specifies whether the method should overwrite the destination file if
it exists.
The third version of the method accepts a different third argument that determines whether the
usual copy animation will be displayed as the file is being copied.
The various versions of the same method differ in the number and/or type of their arguments,
and they’re called overloaded forms of the method. Instead of using multiple method names
for the same basic operation, the overloaded forms of a method allow you to call the same
method name and adjust its behavior by specifying different arguments.
Forms This component lets you access the forms of the current application. You can also
access the application’s forms by name, so the Forms component isn’t the most useful one.
Settings This component lets you access the application settings. These settings apply to the
entire application and are stored in an XML configuration file. The settings are created from
within Visual Studio, and you use the Settings component to read them.
User This component returns information about the current user. The most important
property of the User component is the CurrentPrincipal property, which is an object that
represents the credentials of the current user.
WebServices The WebServices component represents the web services referenced by the
current application.
The My object gives beginners unprecedented programming power and allows you to perform
tasks that would require substantial code if implemented with earlier versions of the language, not
to mention the research it would take to locate the appropriate methods in the Framework. You
can explore the My object on your own and use it as needed. My is not a substitute for learningPetroutsos c01.tex V2 - 01/28/2008 11:43am Page 33
THE BOTTOM LINE 33
the language and the Framework. It can help you initially, but you can’t go far without learning
the methods of the Framework for handling files or any other feature.
Let’s say you want to locate all the files of a specific type in a folder, including its subfolders.
Scanning a folder and its subfolders to any depth is quite a task (you’ll find the code in Chapter
15, ‘‘Accessing Folders and Files’’). You can do the same with a single statement by using the
My object:
Dim files As ReadOnlyCollection(Of String)
files = My.Computer.FileSystem.GetFiles(”D:\Data”, True, ”*.txt”)
The GetFiles method populates the files collection with the pathnames of the text files in
the folder D:\Data and its subfolders. However, it won’t help you if you want to process each file
in place. Moreover, this GetFiles method is synchronous: If the folder contains many subfolders
with many files, it will block the interface until it retrieves all the files. In Chapter 15, you’ll see the
code that retrieves filenames and adds them to a control as it goes along.
If you’re already familiar with VB, you may think that the My object is an aid for the absolute
beginner or the nonprogrammer. This isn’t true. VB is about productivity, and the My object can
help you be more productive with your daily tasks, regardless of your knowledge of the language
or programming skills. If you can use My to save a few (or a few dozen) statements, do it. There’s
no penalty for using the My object, because the compiler replaces the methods of the My object
with the equivalent method calls to the Framework.
The BottomLine
Navigate the integrated development environment of Visual Studio. To simplify the pro-
cess of application development, Visual Studio provides an environment that’s common to
all languages, known as an integrated development environment (IDE). The purpose of the
IDE is to enable the developer to do as much as possible with visual tools, before writing code.
The IDE provides tools for designing, executing, and debugging your applications. It’s your
second desktop, and you’ll be spending most of your productive hours in this environment.
Master It Describe the basic components of the Visual Studio IDE.
Understand the basics of a Windows application. A Windows application consists of a
visual interface and code. The visual interface is what users see at runtime: a form with controls
with which the user can interact— by entering strings, checking or clearing check boxes, click-
ing buttons, and so on. The visual interface of the application is designed with visual tools. The
visual elements incorporate a lot of functionality, but you need to write some code to react to
user actions.
Master It Describe the process of building a simple Windows application.Petroutsos c01.tex V2 - 01/28/2008 11:43am Page 34Petroutsos c02.tex V2 - 01/28/2008 12:12pm Page 35
Chapter 2
Variables and Data Types
This chapter and the next discuss the fundamentals of any programming language: variables
and data types. A variable stores data, which are processed with statements. A program is a list
of statements that manipulate variables. To write even simple applications, you need a basic
understanding of some fundamental topics, such as the data types (the kind of data you can store
in a variable), the scope and lifetime of variables, and how to write procedures and pass arguments
to them. In this chapter, we’ll explore the basic data types of Visual Basic, and in the following one,
you’ll learn about procedures and flow-control statements.
If you’re new to Visual Basic, you may find some material in this chapter less than exciting. It
covers basic concepts and definitions — in general, tedious, but necessary, material. Think of this
chapter as a prerequisite for the following ones. If you need information on core features of the
language as you go through the examples in the rest of the book, you’ll probably find it here.
In this chapter, you’ll learn how to do the following:
◆ Declare and use variables
◆ Use the native data types
◆ Create custom data types
◆ Use arrays
Variables
In Visual Basic, as in any other programming language, variables store values during a program’s
execution. A variable has a name and a value. The variable UserName, for example, can have
the value Joe, and the variable Discount can have the value 0.35. UserName and Discount are
variable names, and Joe and 0.35 are their values. Joe is a string (that is, text or an alphanumeric
value), and 0.35 is a numeric value. When a variable’s value is a string, it must be enclosed in
double quotes. In your code, you can refer to the value of a variable by the variable’s name.
In addition to a name and a value, variables have a data type, which determines what kind
of values we can store to a variable. VB 2008 supports several data types (and they’re discussed
in detail later in this chapter). It’s actually the Common Language Runtime (CLR) that supports
the data types, and they’re common to all languages, not just to Visual Basic. The data type of a
variable is specified when the variable is declared, and you should always declare variables before
using them. To declare a variable, enter the Dim statement, followed by the variable’s name, the As
keyword, and the variable’s type:
Dim Amount As DecimalPetroutsos c02.tex V2 - 01/28/2008 12:12pm Page 36
36 CHAPTER 2 VARIABLES AND DATA TYPES
Decimal is a numeric data type; it can store both integer and noninteger values. For example,
the following statements calculate and display the discount for the amount of $24,500:
Dim Amount As Decimal
Dim Discount As Decimal
Dim DiscountedAmount As Decimal
Amount = 24500
Discount = 0.35
DiscountedAmount = Amount * (1 - Discount)
MsgBox(”Your price is $” & DiscountedAmount.ToString)
If you enter these statements in a button’s Click event handler to test them, the compiler may
underline the statement that assigns the value 0.35 to the Discount variable and generate an
error message. To view the error message, hover the pointer over the underlined segment of the
statement in error. This will happen if the Strict option is on. (I discuss the Strict option, along with
two more options of the compiler, later in this chapter.) By default, the Strict option is off and the
statement should generate an error.
The compiler treats any numeric value with a fractional part as a Double value and detects that
you’re attempting to assign a Double value to a Decimal variable. To convert the numeric value to
the Decimal type, use the following notation:
Discount = 0.35D
As you will see later, the D character at the end of a numeric value indicates that the value
should be treated as a Decimal value, and there are a few more type characters (see Table 2.2 later
in this chapter). I’ve used the Decimal data type here because it’s commonly used in financial
calculations. The message that this expression displays depends on the values of the Discount
and Amount variables. If you decide to offer a better discount, all you have to do is change the
value of the Discount variable. If you didn’t use the Discount variable, you’d have to make many
changes throughout your code. In otherwords, if you coded the line that calculated the discounted
amount as follows, you’d have to look for every line in your code that calculates discounts and
change the discount from 0.35 to another value:
DiscountedAmount = 24500 * (1 - 0.35)
By changing the value of the Discount variable in a single place in your code, the entire
program is up-to-date.
Declaring Variables
In most programming languages, variables must be declared in advance. Historically, the reason
for doing this has been to help the compiler generate the most efficient code. If the compiler knows
all the variables and their types ahead of time, it can produce the most compact and efficient, or
optimized, code. For example, when you tell the compiler that the variable Discount will hold a
number, the compiler sets aside a certain number of bytes for the Discount variable to use.
One of the most popular, yet intensely criticized, features of BASIC was that it didn’t force
the programmer to declare all variables. As you will see, there are more compelling reasons than
speed and efficiency for declaring variables. For example, when a compiler knows the types ofPetroutsos c02.tex V2 - 01/28/2008 12:12pm Page 37
VARIABLES 37
variables in advance, it can catch many errors at design or compile time — errors that otherwise
would surface at runtime. When you declare a variable as Date, the compiler won’t let you assign
an integer value to it.
When programming in VB 2008, you should declare your variables because this is the default
mode, and Microsoft recommends this practice strongly. If you attempt to use an undeclared
variable in your code, VB 2008 will throw an exception. It will actually catch the error as soon
as you complete the line that uses the undeclared variable, underlining it with a wiggly line. It
is possible to change the default behavior and use undeclared variables the way most people did
with earlier versions of VB (you’ll see how this is done in the section ‘‘The Strict, Explicit, and Infer
Options,’’ later in this chapter), but all the examples in this book use explicitly declared variables.
In any case, you’re strongly encouraged to declare your variables.
To declare a variable, use the Dim statement followed by the variable’s name, the As keyword,
and its type, as follows:
Dim meters As Integer
Dim greetings As String
The first variable, meters, will store integers, such as 3 or 1,002; the second variable, greetings,
will store text. You can declare multiple variables of the same or different type in the same line, as
follows:
Dim Qty As Integer, Amount As Decimal, CardNum As String
If you want to declare multiple variables of the same type, you need not repeat the type. Just
separate all the variables of the same type with commas and set the type of the last variable:
Dim Length, Width, Height As Integer, Volume, Area As Double
This statement declares three Integer variables and two Double variables. Double variables
hold fractional values (or floating-point values, as they’re usually called) that are similar to the
Single data type, except that they can represent noninteger values with greater accuracy.
You can use other keywords in declaring variables, such as Private, Public,and Static.
These keywords are called access modifiers because they determine which sections of your code
can access the specific variables and which sections can’t. You’ll learn about these keywords in
later sections of this chapter. In the meantime, bear in mind that all variables declared with the
Dim statement exist in the module in which they were declared. If the variable Count is declared
in a subroutine (an event handler, for example), it exists only in that subroutine. You can’t access
it from outside the subroutine. Actually, you can have a Count variable in multiple procedures.
Each variable is stored locally, and they don’t interfere with one another.
Variable-Naming Conventions
When declaring variables, you should be aware of a few naming conventions. A variable’s name
◆ Must begin with a letter, followed by more letters or digits.
◆ Can’t contain embedded periods or other special punctuation symbols. The only special
character that can appear in a variable’s name is the underscore character.
◆ Mustn’t exceed 255 characters.Petroutsos c02.tex V2 - 01/28/2008 12:12pm Page 38
38 CHAPTER 2 VARIABLES AND DATA TYPES
◆ Must be unique within its scope. This means that you can’t have two identically named
variables in the same subroutine, but you can have a variable named counter in many
different subroutines.
Variable names in VB 2008 are case-insensitive: myAge, myage,and MYAGE all refer to the same
variable in your code. Actually, as you enter variable names, the editor converts their casing so
that they match their declaration.
Variable Initialization
VB 2008 allows you to initialize variables in the same line that declares them. The following
statement declares an Integer variable and initializes it to 3,045:
Dim distance As Integer = 3045
This statement is equivalent to the following two:
Dim distance As Integer
distance = 3045
It is also possible to declare and initialize multiple variables, of the same or different type, on
the same line:
Dim quantity As Integer = 1, discount As Single = 0.25
Type Inference
As Imentioned earlier, one of the trademark features of BASIC, including earlier versions of Visual
Basic, was the ability to use variables without declaring them. It has never been a recommended
practice, yet VB developers loved it. This feature is coming back to the language, only in a safer
manner. VB 2008 allows you to declare variables by assigning values to them. The compiler will
infer the type of the variable from its value and will create a variable of the specific type behind
the scenes. The following statement creates an Integer variable:
Dim count = 2999
To request the variable’s type, use the GetType method. This method returns a Type object,
which represents the variable’s type. The name of the type is given by the ToString property. The
following statement will print the highlighted string in the Immediate window:
Debug.WriteLine(count.GetType.ToString)
System.Int32
The count variable is of the Integer type. If you attempt to assign a value of a different type to
this variable later in your code, such as a date, the editor will underline the value and generate
the warning Value of type ‘Date’ cannot be converted to Integer. The compiler has inferred the type
of the value assigned initially to the variable and created a variable of the same type. That’s why
subsequent statements can’t change the variable’s type. You can turn off type inference by
inserting the following statement at the top of the module:
Option Infer OffPetroutsos c02.tex V2 - 01/28/2008 12:12pm Page 39
VARIABLES 39
Alternatively, you can turn on or off this option in the project’s Properties pages. If the Infer
option is off, the compiler will handle variables declared without a specific type depending on
the Strict option. If the Strict option is off, the compiler will create an Object variable, which can
store any value, even values of different types in the course of the application. If the Strict option
is on, the compiler will reject the declaration; it will underline the variable’s name with a wiggly
line and generate the following warning: Option Strict On requires all variabledeclarationstohavean
As clause.
For more information on the various variable declaration–related options of the compiler, see
the section ‘‘The Strict, Explicit, and Infer Options,’’ later in this chapter. In the following sections,
you’ll explore the various data types of Visual Basic, and I will use explicit declarations, which is
the recommended best practice for creating and using variables in your code.
Types of Variables
Visual Basic recognizes the following five categories of variables:
◆ Numeric
◆ String
◆ Boolean
◆ Date
◆ Object
The two major variable categories are numeric and string. Numeric variables store numbers,
and string variables store text. Object variables can store any type of data. Why bother to specify
the type if one type suits all? On the surface, using object variables might seem like a good idea,
but they have their disadvantages. Integer variables are optimized for storing integers, and date
variables are optimized for storing dates. Before VB can use an object variable, it must determine
its type and perform the necessary conversions. If the variable is declared with a specific type,
these conversions are not necessary.
We begin our discussion of variable types with numeric variables. Text is stored in string
variables, but numbers can be stored in many formats, depending on the size of the number and
its precision. That’s why there are many types of numeric variables. The String and Date data
types are much richer in terms of the functionality they expose, and are discussed in more detail
in Chapter 13, ‘‘Handling Strings, Characters, and Dates.’’
Numeric Variables
You’d expect that programming languages would use the same data type for numbers. After all,
a number is a number. But this couldn’t be further from the truth. All programming languages
provide a variety of numeric data types, including the following:
◆ Integers (there are several integer data types)
◆ Decimals
◆ Single, or floating-point numbers with limited precision
◆ Double, or floating-point numbers with extreme precision
Decimal, Single, and Double are the three basic data types for storing floating-point
numbers (numbers with a fractional part). The Double data type can represent these numbers
more accurately than the Single type and is used almost exclusively in scientific calculations.Petroutsos c02.tex V2 - 01/28/2008 12:12pm Page 40
40 CHAPTER 2 VARIABLES AND DATA TYPES
The Integer data types store whole numbers. The data type of your variable can make a
difference in the results of the calculations. The proper variable types are determined by the
nature of the values they represent, and the choice of data type is frequently a trade-off
between precision and speed of execution (less-precise data types are manipulated faster).
Visual Basic supports the numeric data types shown in Table 2.1. In the Data Type column,
I show the name of each data type and the corresponding keyword in parentheses.
Integer Variables
There are three types of variables for storing integers, and they differ only in the range of numbers
each can represent. As you understand, the more bytes a type takes, the larger values it can hold.
The type of Integer variable you’ll use depends on the task at hand. You should choose the type
that can represent the largest values you anticipate will come up in your calculations. You can go
for the Long type, to be safe, but Long variables are four times as large as Short variables, and it
takes the computer longer to process them.
The statements in Listing 2.1 will help you understand when to use the various Integer data
types. Each numeric data type exposes the MinValue and MaxValue properties, which return the
minimum and maximum values, respectively, that can be represented by the corresponding data
type. Values of the Short (Int16) type can be stored in Integer (Int32) and Long (Int64)variables,
but the reverse is not true. If you attempt to store a Long value to an Integer variable, an error will
be generated and the compiler will underline the offending line with a wiggly line. I have included
comments after each statement to explain the errors produced by some of the statements.
Listing 2.1: Experimentingwith the Ranges of Numeric Variables
Dim shortInt As Int16
Dim Int As Int32
Dim longInt As Int64
Debug.WriteLine(Int16.MinValue)
Debug.WriteLine(Int16.MaxValue)
Debug.WriteLine(Int32.MinValue)
Debug.WriteLine(Int32.MaxValue)
Debug.WriteLine(Int64.MinValue)
Debug.WriteLine(Int64.MaxValue)
shortInt = Int16.MaxValue + 1
’ ERROR, exceeds the maximum value of the Short data type
Int = Int16.MaxValue + 1
’ OK, is within the range of the Integer data type
Int = Int32.MaxValue + 1
’ ERROR, exceeds the maximum value of the Integer data type
Int = Int32.MinValue - 1
’ ERROR, exceeds the minimum value of the Integer data type
longInt = Int32.MaxValue + 1
’ OK, is within the range of the Long data type
longInt = Int64.MaxValue + 1
’ ERROR, exceeds the range of all Integer data typesPetroutsos c02.tex V2 - 01/28/2008 12:12pm Page 41
VARIABLES 41
Table 2.1: Visual Basic Numeric Data Types
Data Type Memory Representation Stores
Byte (Byte) 1 byte Integers in the range 0 to 255.
Signed Byte (SByte) 1 byte Integers in the range −128 to 127.
Short (Int16) 2 bytes Integer values in the range −32,768
to 32,767.
Integer (Int32) 4 bytes Integer values in the range
−2,147,483,648 to 2,147,483,647.
Long (Int64) 8 bytes Integer values in the range
−9,223,372,036,854,755,808 to
9,223,372,036,854,755,807.
Unsigned Short (UShort) 2 bytes Positive integer values in the range 0
to 65,535.
Unsigned Integer (UInteger) 4 bytes Positive integers in the range 0 to
4,294,967,295.
Unsigned Long (ULong) 8 bytes Positive integers in the range 0 to
18,446,744,073,709,551,615.
Single Precision (Single) 4 bytes Single-precision floating-point
numbers. It can represent negative
numbers in the range −3.402823E38
to −1.401298E-45 and positive
numbers in the range 1.401298E-45
to 3.402823E38. The value 0 can’t
be represented precisely (it’s a
very, very small number, but not
exactly 0).
Double Precision (Double) 8 bytes Double-precision floating-point
numbers. It can represent negative
numbers in the range
−1.79769313486232E308 to
−4.94065645841247E-324 and
positive numbers in the range
4.94065645841247E-324 to
1.79769313486232E308.
Decimal (Decimal) 16 bytes Integer and floating-point numbers
scaled by a factor in the range from
0 to 28. See the description of the
Decimal data type for the range of
values you can store in it.Petroutsos c02.tex V2 - 01/28/2008 12:12pm Page 42
42 CHAPTER 2 VARIABLES AND DATA TYPES
The six WriteLine statements will print the minimum and maximum values you can represent
with the various Integer data types. The following statement attempts to assign to a Short integer
variable a value that exceeds the largest possible value you can represent with the Short data type,
and it will generate an error. The editor will underline the incorrect statement, and if you hover
the pointer over the statement, you’ll see the error description: Constant expression not representable
in type Short. If you attempt to store the same value to an Integer variable, there will be no problem
because this value is well within the range of the Integer data type.
The next two statements attempt to store to an Integer variable two values that are also outside
of the range that an integer can represent. The first value exceeds the range of positive values, and
the second exceeds the range of negative values. If you attempt to store these values to a Long
variable, there will be no problem. If you exceed the range of values that can be represented by
the Long data type, you’re out of luck. This value can’t be represented as an integer, and you must
store it in one of the variable types discussed in the next sections.
Single- and Double-Precision Numbers
The names Single and Double come from single-precision and double-precision numbers.
Double-precision numbers are stored internally with greater accuracy than single-precision
numbers. In scientific calculations, you need all the precision you can get; in those cases, you
should use the Double data type.
The result of the operation 1 / 3 is 0.333333... (an infinite number of digits 3). You could fill
256MB of RAMwith 3 digits, and the result would still be truncated. Here’s a simple example that
demonstrates the effects of truncation:
In a button’s Click event handler, declare two variables as follows:
Dim a As Single, b As Double
Then enter the following statements:
a=1/3
Debug.WriteLine(a)
Run the application, and you should get the following result in the Output window:
.3333333
There are seven digits to the right of the decimal point. Break the application by pressing
Ctrl+Break and append the following lines to the end of the previous code segment:
a=a* 100000
Debug.WriteLine(a)
This time, the following value will be printed in the Output window:
33333.34Petroutsos c02.tex V2 - 01/28/2008 12:12pm Page 43
VARIABLES 43
The result is not as accurate as you might have expected initially — it isn’t even rounded
properly. If you divide a by 100,000, the result will be
0.3333334
This number is different from the number we started with (0.3333333). The initial value was
rounded when we multiplied it by 100,000 and stored it in a Single variable. This is an important
point in numeric calculations, and it’s called error propagation. In long sequences of numeric calcu-
lations, errors propagate. Even if you can tolerate the error introduced by the Single data type in a
single operation, the cumulative errors might be significant.
Let’s perform the same operations with double-precision numbers, this time using the
variable b. Add these lines to the button’s Click event handler:
b=1/3
Debug.WriteLine(b)
b=b* 100000
Debug.WriteLine(b)
This time, the following numbers are displayed in the Output window:
0.333333333333333
33333.3333333333
The results produced by the double-precision variables are more accurate.
Why are such errors introduced in our calculations? The reason is that computers store
numbers internally with two digits: zero and one. This is very convenient for computers
because electronics understand two states: on and off. As a matter of fact, all the statements
are translated into bits (zeros and ones) before the computer can understand and execute them.
The binary numbering system used by computers is not much different from the decimal system
we humans use; computers just use fewer digits. We humans use 10 different digits to represent
any number, whole or fractional, because we have 10 fingers (in effect, computers count with
just two fingers). Just as with the decimal numbering system, in which some numbers can’t be
precisely represented, there are also numbers that can’t be represented precisely in the binary
system.
Let me give you a more illuminating example. Create a single-precision variable, a,anda
double-precision variable, b, and assign the same value to them:
Dim a As Single, b As Double
a = 0.03007
b = 0.03007
Then print their difference:
Debug.WriteLine(a-b)Petroutsos c02.tex V2 - 01/28/2008 12:12pm Page 44
44 CHAPTER 2 VARIABLES AND DATA TYPES
If you execute these lines, the result won’t be zero! It will be −6.03199004634014E-10. This is
a very small number that can also be written as 0.000000000603199004634014. Because different
numeric types are stored differently in memory, they don’t quite match. What this means to you
is that all variables in a calculation should be of the same type.
Eventually, computers will understand mathematical notation and will not convert all numeric
expressions into values, as they do today. If you multiply the expression 1/3 by 3, the result
should be 1. Computers, however, must convert the expression 1/3 into a value before they can
multiply it by 3. Because 1/3 can’t be represented precisely, the result of the (1/3) × 3 will not be
exactly 1. If the variables a and b are declared as Single or Double, the following statements will
print 1:
a=3
b=1/a
Debug.WriteLine(a * b)
If the two variables are declared as Decimal, however, the result will be a number very close
to 1, but not exactly 1 (it will be 0.9999999999999999999999999999— there are 28 digits after the
decimal point).
The Decimal Data Type
Variables of the Decimal type are stored internally as integers in 16 bytes and are scaled by a power
of 10. The scaling power determines the number of decimal digits to the right of the floating point,
and it’s an integer value from 0 to 28. When the scaling power is 0, the value is multiplied by
100
, or 1, and it’s represented without decimal digits. When the scaling power is 28, the value is
divided by 1028 (which is 1 followed by 28 zeros — an enormous value), and it’s represented with
28 decimal digits.
The largest possible value you can represent with a Decimal value is an integer: 79,228,162,
514,264,337,593,543,950,335. The smallest number you can represent with a Decimal variable is
the negative of the same value. These values use a scaling factor of 0. When the scaling factor
is 28, the largest value you can represent with a Decimal variable is quite small, actually. It’s
7.9228162514264337593543950335 (and the smallest value is the same with a minus sign). This
is a very small numeric value (not quite 8), but it’s represented with extreme accuracy. The
number zero can’t be represented precisely with a Decimal variable scaled by a factor of 28.
The smallest positive value you can represent with the same scaling factor is 0.00...01
(there are 27 zeros between the decimal period and the digit 1) — an extremely small value,
but still not quite zero. The more accuracy you want to achieve with a Decimal variable, the
smaller the range of available values you have at your disposal — just as with everything else
in life.
When using decimal numbers, the compiler keeps track of the decimal digits (the digits
following the decimal point) and treats all values as integers. The value 235.85 is represented
as the integer 23585, but the compiler knows that it must scale down the value by 100 when
it finishes using it. Scaling down by 100 (that is, 102) corresponds to shifting the decimal
point by two places. First, the compiler multiplies this value by 100 to make it an integer. Then,
it divides it by 100 to restore the original value. Let’s say that you want to multiply the following
values:
328.558 * 12.4051Petroutsos c02.tex V2 - 01/28/2008 12:12pm Page 45
VARIABLES 45
First, you must turn them into integers. You must remember that the first number has three
decimal digits, and the second number has four decimal digits. The result of the multiplication
will have seven decimal digits. So you can multiply the following integer values:
328558 * 124051
and then treat the last seven digits of the result as decimals. Use the Windows Calculator (in
the Scientific view) to calculate the previous product. The result is 40,757,948,458. The actual
value after taking into consideration the decimal digits is 4,075.7948458. This is how the compiler
manipulates the Decimal data type. Insert the following lines in a button’s Click event handler
and execute the program:
Dim a As Decimal = 328.558D
Dim b As Decimal = 12.4051D
Dim c As Decimal
c=a*b
Debug.WriteLine(c.ToString)
The D character at the end of the two numeric values specifies that the numbers should be
converted into Decimal values. By default, every value with a fractional part is treated as a Double
value. Assigning a Double value to a Decimal variable will produce an error if the Strict option is
on, so we must specify explicitly that the two values should be converted to the Decimal type. The
D character at the end of the value is called a type character. Table 2.2 lists all of them.
Table 2.2: Type Characters
Type Character Description Example
C Converts value to a Char type Dim ch As String = ‘‘A’’c
D or @ Converts value to a Decimal type Dim price As Decimal = 12.99D
R or # Converts value to a Double type Dim pi As Double = 3.14 R
I or % Converts value to an Integer type Dim count As Integer = 99I
L or & Converts value to a Long type Dim distance As Long = 1999L
S Converts value to a Short type Dim age As Short = 1S
F or ! Converts value to a Single type Dim velocity As Single = 74.99F
If you perform the same calculations with Single variables, the result will be truncated (and
rounded) to three decimal digits: 4,075.795. Notice that the Decimal data type didn’t introduce
any rounding errors. It’s capable of representing the result with the exact number of decimal
digits. This is the real advantage of Decimals, which makes them ideal for financial applications.
For scientific calculations, you must still use Doubles. Decimal numbers are the best choice for
calculations that require a specific precision (such as four or eight decimal digits).Petroutsos c02.tex V2 - 01/28/2008 12:12pm Page 46
46 CHAPTER 2 VARIABLES AND DATA TYPES
Infinity and Other Oddities
The Framework can represent two very special values, which may not be numeric values
themselves but are produced by numeric calculations: NaN (not a number) and Infinity. If your
calculations produce NaN or Infinity, you should confirm the data and repeat the calculations,
or give up. For all practical purposes, neither NaN nor Infinity can be used in everyday business
calculations.
Not a Number (NaN)
NaN is not new. Packages such as Wolfram Mathematica and Microsoft Excel have been using
it for years. The value NaN indicates that the result of an operation can’t be defined: It’s not a
regular number, not zero, and not infinity. NaN ismore of amathematical concept rather than a value
you can use in your calculations. The Log() function, for example, calculates the logarithm of
positive values. By definition, you can’t calculate the logarithm of a negative value. If the argument
you pass to the Log() function is a negative value, the function will return the value NaN to indicate
that the calculations produced an invalid result. You may find it annoying that a numeric function
returns a non-numeric value, but it’s better than throwing an exception. Even if you don’t detect this
condition immediately, your calculations will continue and they will all produce NaN values.
Some calculations produce undefined results, such as infinity. Mathematically, the result of
dividing any number by zero is infinity. Unfortunately, computers can’t represent infinity, so they
produce an error when you request a division by zero. VB 2008 will report a special value, which
isn’t a number: the Infinity value. If you call the ToString method of this value, however, it will
return the string Infinity. Let’s generate an Infinity value. Start by declaring a Double variable,
dblVar:
Dim dblVar As Double = 999
Then divide this value by zero:
Dim infVar as Double
infVar = dblVar / 0
and display the variable’s value:
MsgBox(infVar)
The string Infinity will appear in a message box. This string is just a description; it tells you
that the result is not a valid number (it’s a very large number that exceeds the range of numeric
values that can be represented with any data type), but it shouldn’t be used in other calculations.
However, you can use the Infinity value in arithmetic operations. Certain operations with infinity
make sense; others don’t. If you add a number to infinity, the result is still infinity (any number,
even an arbitrarily large one, can still be increased). If you divide a value by infinity, you’ll get
the zero value, which also makes sense. If you divide one Infinity value by another Infinity value,
you’ll get the second odd value, NaN.Petroutsos c02.tex V2 - 01/28/2008 12:12pm Page 47
VARIABLES 47
Another calculation that will yield a non-number is the division of a very large number by a
very small number. If the result exceeds the largest value that can be represented with the Double
data type, the result is Infinity. Declare three variables as follows:
Dim largeVar As Double = 1E299
Dim smallVar As Double = 1E-299
Dim result As Double
The notation 1E299 means 10 raised to the power of 299, which is an extremely large number.
Likewise, 1E-299 means 10 raised to the power of −299, which is equivalent to dividing 10 by a
number as large as 1E299.
Then divide the large variable by the small variable and display the result:
result = largeVar / smallVar
MsgBox(result)
The result will be Infinity. If you reverse the operands (that is, you divide the very small by the
very large variable), the result will be zero. It’s not exactly zero, but the Double data type can’t
accurately represent numeric values that are very, very close to zero.
You can also produce an Infinity value by multiplying a very large (or very small) number by
itself many times. But clearly, the most absurd method of generating an Infinity value is to assign
the Double.PositiveInfinity or Double.NegativeInfinity value to a variable!
The result of the division 0 / 0, for example, is not a numeric value. If you attempt to enter the
statement 0/0 in your code, however, VB will catch it even as you type, and you’ll get the error
message Division by zero occurs in evaluating this expression.
To divide zero by zero, set up two variables as follows:
Dim var1, var2 As Double
Dim result As Double
var1 = 0
var2 = 0
result = var1 / var2
MsgBox(result)
If you execute these statements, the result will be NaN. Any calculations that involve the result
variable will yield NaN as a result. The following statements will produce a NaN value:
result = result + result
result = 10 / result
result = result + 1E299
MsgBox(result)
If you make var2 a very small number, such as 1E-299, the result will be zero. If you make var1
a very small number, the result will be Infinity.
For most practical purposes, Infinity is handled just like NaN. They’re both numbers that
shouldn’t occur in business applications (unless you’re projecting the national deficit in thePetroutsos c02.tex V2 - 01/28/2008 12:12pm Page 48
48 CHAPTER 2 VARIABLES AND DATA TYPES
next 50 years), and when they do, it means that you must double-check your code or your data.
They are much more likely to surface in scientific calculations, and they must be handled
with the statements described in the next section.
Testing for Infinity and NaN
To find out whether the result of an operation is a NaN or Infinity, use the IsNaN
and IsInfinity methods of the Single and Double data types. The Integer data
type doesn’t support these methods, even if it’s possible to generate Infinity and NaN
results with integers. If the IsInfinity method returns True, you can further examine
the sign of the Infinity value with the IsNegativeInfinity and IsPositiveInfinity
methods.
In most situations, you’ll display a warning and terminate the calculations. The statements
of Listing 2.2 do just that. Place these statements in a button’s Click event handler and run the
application.
Listing 2.2: Handling NaN and Infinity Values
Dim var1, var2 As Double
Dim result As Double
var1 = 0
var2 = 0
result = var1 / var2
If Double.IsInfinity(result) Then
If Double.IsPositiveInfinity(result) Then
MsgBox(”Encountered a very large number. Can’t continue”)
Else
MsgBox(”Encountered a very small number. Can’t continue”)
End If
Else
If Double.IsNaN(result) Then
MsgBox(”Unexpected error in calculations”)
Else
MsgBox(”The result is:”& result.ToString)
End If
End If
This listing will generate a NaN value. Set the value of the var1 variable to 1 to generate
a positive Infinity value, or to −1 to generate a negative Infinity value. As you can see, the
IsInfinity, IsPositiveInfinity, IsNegativeInfinity,and IsNaN methods require that the
variable be passed as an argument.
If you change the values of the var1 and var2 variables to the following values and execute the
application, you’ll get the message Encountered a very large number:
var1 = 1E+299
var2 = 1E-299Petroutsos c02.tex V2 - 01/28/2008 12:12pm Page 49
VARIABLES 49
If you reverse the values, you’ll get the message Encountered a very small number. In any case,
the program will terminate gracefully and let you know the type of problem that prevents the
completion of the calculations.
Byte Variables
None of the previous numeric types is stored in a single byte. In some situations, however, data
are stored as bytes, and you must be able to access individual bytes. The Byte data type holds
an integer in the range of 0 to 255. Bytes are frequently used to access binary files, image and
sound files, and so on. Note that you no longer use bytes to access individual characters. Unicode
characters are stored in two bytes.
To declare a variable as a Byte, use the following statement:
Dim n As Byte
The variable n can be used in numeric calculations too, but you must be careful not to assign the
result to another Byte variable if its value might exceed the range of the Byte type. If the variables
A and B are initialized as follows:
Dim A As Byte, B As Byte
A = 233
B=50
the following statement will produce an overflow exception:
Debug.WriteLine(A + B)
The same will happen if you attempt to assign this value to a Byte variable with the following
statement:
B=A+B
The result (283) can’t be stored in a single byte. Visual Basic generates the correct answer, but
it can’t store it into a Byte variable.
Boolean Operations with Bytes
The operators that won’t cause overflows are the Boolean operators AND, OR, NOT,and XOR,which
are frequently used with Byte variables. These aren’t logical operators that return True or False;
they combine the matching bits in the two operands and return another byte. If you combine the
numbers 199 and 200 with the AND operator, the result is 192. The two values in binary format
are 11000111 and 11001000. If you perform a bitwise AND operationonthesetwovalues,theresultis
11000000, which is the decimal value 192.
In addition to the Byte data type, VB 2008 provides a Signed Byte data type, SByte, which can
represent signed values in the range from −128 to 127. The bytes starting with the 1 bit represent
negative values. The range of positive values is less by one than the range of values of negative
values, because the value 0 is considered a positive value (its first bit is 0).Petroutsos c02.tex V2 - 01/28/2008 12:12pm Page 50
50 CHAPTER 2 VARIABLES AND DATA TYPES
Boolean Variables
The Boolean data type stores True/False values. Boolean variables are, in essence, integers that
take the value −1 (for True) and 0 (for False). Actually, any nonzero value is considered True.
Boolean variables are declared as
Dim failure As Boolean
and they are initialized to False. Boolean variables are used in testing conditions, such as the
following:
Dim failure As Boolean = False
’ other statements ...
If failure Then MsgBox(”Couldn’t complete the operation”)
They are also combined with the logical operators And, Or, Not,and Xor.The Not operator
toggles the value of a Boolean variable. The following statement is a toggle:
running = Not running
If the variable running is True, it’s reset to False, and vice versa. This statement is a shorter
way of coding the following:
Dim running As Boolean
If running = True Then
running = False
Else
running = True
End If
Boolean operators operate on Boolean variables and return another Boolean as their result. The
following statements will display a message if one (or both) of the variables ReadOnly and Hidden
are True (presumably these variables represent the corresponding attributes of a file):
If ReadOnly Or Hidden Then
MsgBox(”Couldn’t open the file”)
Else
{ statements to open and process file}
End If
The condition of the If statement combines the two Boolean values with the Or operator.
If one or both of them are True, the parenthesized expression is True. This value is negated with
the Not operator, and the If clause is executed only if the result of the negation is True. If ReadOnly
is True and Hidden is False, the expression is evaluated as
If Not (True Or False)Petroutsos c02.tex V2 - 01/28/2008 12:12pm Page 51
VARIABLES 51
(True Or False) is True, which reduces the expression to
If Not True
which, in turn, is False.
String Variables
The String data type stores only text, and string variables are declared as follows:
Dim someText As String
You can assign any text to the variable someText. You can store nearly 2GB of text in a string
variable (that’s 2 billion characters, and is much more text than you care to read on a computer
screen). The following assignments are all valid:
Dim aString As String
aString = ”Now is the time for all good men to come ” &
” to the aid of their country”
aString = ””
aString = ”There are approximately 25,000 words in this chapter”
aString = ”25,000”
The second assignment creates an empty string, and the last one creates a string that just
happens to contain numeric digits, which are also characters. The difference between these two
variables is that they hold different values:
Dim aNumber As Integer = 25000
Dim aString As String = ”25,000”
The aString variable holds the characters 2, 5, comma, 0, 0, and 0; and aNumber holds a
single numeric value. However, you can use the variable aString in numeric calculations, and the
variable aNumber in string operations. VB will perform the necessary conversions as long as the
Strict option is off.
The String data type and its text manipulation methods are discussed in detail in
Chapter 13.
Character Variables
Character variables store a single Unicode character in two bytes. In effect, characters are
Unsigned Short integers (UInt16); you can use the CChar() function to convert integers
to characters and use the CInt() function to convert characters to their equivalent
integer values.
To declare a Character variable, use the Char keyword:
Dim char1, char2 As CharPetroutsos c02.tex V2 - 01/28/2008 12:12pm Page 52
52 CHAPTER 2 VARIABLES AND DATA TYPES
You can initialize a Character variable by assigning either a character or a string to it. In the
latter case, only the first character of the string is assigned to the variable. The following statements
will print the characters a and A to the Output window:
Dim char1 As Char = ”a”, char2 As Char = ”ABC”
Debug.WriteLine(char1)
Debug.WriteLine(char2)
These statements will work only if the Strict option is off. If it’s on, the values assigned to the
char1 and char2 variables will be marked in error. To fix the error that prevents the compilation
of the code, change the Dim statement as follows:
Dim char1 As Char = ”a”c, char2 As Char = ”A”c
When the Strict option is on, you can’t assign a string to a Char variable and expect that only
the first character of the string will be used.
The Integer values that correspond to the English characters are the ANSI (American National
Standards Institute) codes of the equivalent characters. The following statement will print the
value 65:
Debug.WriteLine(Convert.ToInt32(”a”))
If you convert the Greek character alpha (α) to an integer, its value is 945. The Unicode value of
the famous character π is 960.
Character variables are used in conjunction with strings. You’ll rarely save real data as char-
acters. However, you might have to process the individual characters in a string, one at a time.
The Char data type exposes a number of interesting methods for manipulating characters, and
they’re presented in detail in Chapter 13. Let’s say the string variable password holds a user’s new
password, and you require that passwords contain at least one special symbol. The code segment
of Listing 2.3 scans the password and rejects it if it contains letters and digits only.
Listing 2.3: Processing Individual Characters
Dim password As String, ch As Char
Dim i As Integer
Dim valid As Boolean = False
While Not valid
password = InputBox(”Please enter your password”)
For i = 0 To password.Length - 1
ch = password.Chars(i)
If Not Char.IsLetterOrDigit(ch) Then
valid = True
Exit For
End If
Next
If valid Then
MsgBox(”You new password will be activated immediately!”)Petroutsos c02.tex V2 - 01/28/2008 12:12pm Page 53
VARIABLES 53
Else
MsgBox(”Your password must contain at least one special symbol!”)
End If
End While
If you are not familiar with the If...Then, For...Next,or While...End While structures, you
can read their descriptions in the following chapter.
Thecodepromptstheuserwithaninputboxtoenterapassword.The valid variable is Boolean
and it’s initialized to False. (You don’t have to initialize a Boolean variable to False because this is
its default initial value, but it does make the code easier to read.) It’s set to True from within the
body of the loop, only if the password contains a character that is not a letter or a digit. We set it
to False initially, so the While...End While loop will be executed at least once. This loop will keep
prompting the user until a valid password is entered.
The For...Next loop scans the string variable password, one letter at a time. At each iteration,
the next letter is copied into the ch variable. The Chars property of the String data type is an array
that holds the individual characters in the string (another example of the functionality built into
the data types).
Then the program examines the current character. The IsLetterOrDigit method of the Char
data type returns True if a character is either a letter or a digit. If the current character is a symbol,
the program sets the valid variable to True so that the outer loop won’t be executed again, and
it exits the For...Next loop. Finally, it prints the appropriate message, and either prompts for
another password or quits.
The Char class and its methods are discussed in more detail in Chapter 13.
Date Variables
Date and time values are stored internally in a special format, but you don’t need to know the
exact format. They are double-precision numbers: the integer part represents the date, and the
fractional part represents the time. A variable declared as Date with a statement like the following
can store both date and time values:
Dim expiration As Date
The following are all valid assignments:
expiration = #01/01/2008#
expiration = #8/27/2008 6:29:11 PM#
expiration = ”July 2, 2008”
expiration = Today()
By the way, the Today() function returns the current date and time, while the Now() function
returns the current date. You can also retrieve the current date by calling the Today property of
the Date data type: Date.Today.
The pound sign tells Visual Basic to store a date value to the expiration variable,justasthe
quotes tell Visual Basic that the value is a string. You can store a date as a string to a Date variable,
but it will be converted to the appropriate format. If the Strict option is on, you can’t specify dates
by using the Long date format (as in the third statement of this example).Petroutsos c02.tex V2 - 01/28/2008 12:12pm Page 54
54 CHAPTER 2 VARIABLES AND DATA TYPES
The date format is determined by the Regional Settings (found in the Control Panel). In the
United States, the format is mm/dd/yy. (In other countries, the format is dd/mm/yy.) If you assign
an invalid date to a date variable, such as 23/04/2002, the statement will be underlined and an
error message will appear in the Task List window. The description of the error is Date constant is
not valid.
The Date data type is extremely flexible; Visual Basic knows how to handle date and time
values, so you won’t have to write complicated code to perform the necessary conversions. To
manipulate dates and times, use the members of the Date type, which are discussed in detail in
Chapter 13, or the date and time functions of VB 6, which are still supported by VB 2008.
You can also perform arithmetic operations with date values. VB recognizes your intention to
subtract dates and it properly evaluates their difference. The result is a TimeSpan object, which
represents a time interval. If you execute the following statements, the value 638.08:49:51.4970000
will appear in the Output window:
Dim d1, d2 As Date
d1 = Now
d2 = #1/1/2004#Debug.WriteLine(d1 - d2)
The value of the TimeSpan object represents an interval of 638 days, 8 hours, 49 minutes, and
51.497 seconds.
Data Type Identifiers
Finally, you can omit the As clause of the Dim statement, yet create typed variables, with the
variable declaration characters, or data type identifiers. These characters are special symbols that
you append to the variable name to denote the variable’s type. To create a string variable, you can
use this statement:
Dim myText$
The dollar sign signifies a string variable. Notice that the name of the variable includes
the dollar sign — it’s myText$,not myText. To create a variable of a particular type, use
one of the data declaration characters shown in Table 2.3. (Not all data types have their own
identifiers.)
Using type identifiers doesn’t help to produce the cleanest and easiest-to-read code. They’re
relics from really old versions of BASIC, and if you haven’t used them in the past, there’s no really
good reason to start using them now.
The Strict, Explicit, and Infer Options
The Visual Basic compiler provides three options that determine how it handles variables:
◆ The Explicit option indicates whether you will declare all variables.
◆ The Strict option indicates whether all variables will be of a specific type.
◆ The Infer option indicates whether the compiler should determine the type of a variable
from its value.
These options have a profound effect on the way you declare and use variables, and you should
understand what they do. By exploring these settings, you will also understand a little better howPetroutsos c02.tex V2 - 01/28/2008 12:12pm Page 55
VARIABLES 55
Table 2.3: Data Type Definition Characters
Symbol Data Type Example
$ String A$, messageText$
% Integer (Int32) counter%, var%
& Long (Int64) population&, colorValue&
! Single distance!
# Double ExactDistance
@ Decimal Balance@
the compiler handles variables. It’s recommended that you turn on all three of them, but old VB
developers may not follow this advice.
VB 2008 doesn’t require that you declare your variables, but the default behavior is to throw an
exception if you attempt to use a variable that hasn’t been previously declared. If an undeclared
variable’s name appears in your code, the editor will underline the variable’s name with a wiggly
line, indicating that it caught an error. The description of the error will appear in the Task List
below the code window. If you rest the pointer over the segment of the statement in question, you
will see the description of the error in a ToolTip box.
To change the default behavior, you must insert the following statement at the beginning of
the file:
Option Explicit Off
The Option Explicit statement must appear at the very beginning of the file. This setting
affects the code in the current module, not in all files of your project or solution. You can turn
on the Strict (as well as the Explicit) option for an entire solution. Open the solution’s properties
dialog box (right-click the solution’s name in Solution Explorer and select Properties), select the
Compile tab, and set the Strict and Explicit options accordingly, as shown in Figure 2.1.
You can also set default values for the Explicit option (as well as for Strict and Infer) for
all projects through the Options dialog box of the IDE. To open this dialog box, choose the
Options command from the Tools menu. When the dialog box appears, select the VB Defaults tab
under Projects And Solutions, as shown in Figure 2.2. Here you can set the default values for all
four options. You can still change the default values for specific projects through the project’s
Properties pages.
The way undeclared variables are handled by VB 2008 is determined by the Explicit and Strict
options, which can be either on or off. The Explicit option requires that all variables used in the
code are declared before they’re used. The Strict option requires that variables are declared with a
specific type. In other words, the Strict option disallows the use of generic variables that can store
any data type.
The default value of the Explicit statement is On. This is also the recommended value,
and you should not make a habit of changing this setting. In the section ‘‘Why Declare
Variables?’’ later in this chapter, you will see an example of the pitfalls you’ll avoid by
declaring your variables. By setting the Explicit option to Off, you’re telling VB that youPetroutsos c02.tex V2 - 01/28/2008 12:12pm Page 56
56 CHAPTER 2 VARIABLES AND DATA TYPES
intend to use variables without declaring them. As a consequence, VB can’t make any assumption
about the variable’s type, so it uses a generic type of variable that can hold any type of
information. These variables are called Object variables, and they’re equivalent to the old
variants.
Figure 2.1
Setting the
variable-related options
on the project’s
Properties pages
Figure 2.2
Setting the
variable-related options
in the Visual Studio
Options dialog box
While the option Explicit is set to Off, every time Visual Basic runs into an undeclared variable
name, it creates a new variable on the spot and uses it. The new variable’s type is Object, the
generic data type that can accommodate all other data types. Using a new variable in your codePetroutsos c02.tex V2 - 01/28/2008 12:12pm Page 57
VARIABLES 57
is equivalent to declaring it without type. Visual Basic adjusts its type according to the value you
assign to it. Create two variables, var1 and var2, by referencing themin your code with statements
like the following ones:
var1 = ”Thank you for using Fabulous Software”
var2 = 49.99
The var1 variable is a string variable, and var2 is a numeric one. You can verify this with the
GetType method, which returns a variable’s type. The following statements print the highlighted
types shown below each statement:
Debug.WriteLine ”Variable var1 is ” & var1.GetType().ToString
Variable var1 is System.String
Debug.WriteLine ”Variable var2 is ” & var2.GetType().ToString
Variable var2 is System.Double
Later in the same program, you can reverse the assignments:
var1 = 49.99
var2 = ”Thank you for using Fabulous Software”
If you execute the preceding statements again, you’ll see that the types of the variables
have changed. The var1 variable is now a Double, and var2 is a String. The type of a
generic variable is determined by the variable’s contents and it can change in the course
of the application. Of course, changing a variable’s type at runtime doesn’t come without
a performance penalty (a small one, but nevertheless some additional statements must be
executed).
Another related option is the Strict option, which is off by default. The Strict option tells
the compiler whether the variables should be strictly typed. A strictly typed variable must
be declared with a specific type and it can accept values of the same type only. With the Strict
option set to Off, you can use a string variable that holds a number in a numeric
calculation:
Dim a As String = ”25000”
Debug.WriteLine a / 2
The last statement will print the value 12500 in the Immediate window. Likewise, you can use
numeric variables in string calculations:
Dim a As Double = 31.03
a=a+”1”
If you turn the Strict option on by inserting the following statement at the beginning of the file,
you won’t be able to mix and match variable types:
Option Strict OnPetroutsos c02.tex V2 - 01/28/2008 12:12pm Page 58
58 CHAPTER 2 VARIABLES AND DATA TYPES
If you attempt to execute any of the last two code segments while the Strict option is on, the
compiler will underline a segment of the statement to indicate an error. If you rest the pointer over
the underlined segment of the code, the following error message will appear in a tip box:
Option strict disallows implicit conversions from String to Double
(or whatever type of conversion is implied by the statement).
When the Strict option is set to On, the compiler doesn’t disallow all implicit conversions
between data types. For example, it will allow you to assign the value of an integer to a Long,
but not the opposite. The Long value might exceed the range of values that can be represented by
an Integer variable. You will find more information on implicit conversions in the section titled
‘‘Widening and Narrowing Conversions,’’ later in this chapter.
Object Variables
Variants — variables without a fixed data type— were the bread and butter of VB programmers
up to version 6. Variants are the opposite of strictly typed variables: They can store all types of
values, from a single character to an object. If you’re starting with VB 2008, you should use strictly
typed variables. However, variants are a major part of the history of VB, and most applications
out there (the ones you may be called to maintain) use them. I will discuss variants briefly in this
section and show you what was so good (and bad) about them.
Variants, or object variables, were themost flexible data types because they could accommodate
all other types. A variable declared as Object (or a variable that hasn’t been declared at all) is
handled by Visual Basic according to the variable’s current contents. If you assign an integer value
to an object variable, Visual Basic treats it as an integer. If you assign a string to an object variable,
Visual Basic treats it as a string. Variants can also hold different data types in the course of the
same program. Visual Basic performs the necessary conversions for you.
To declare a variant, you can turn off the Strict option and use the Dim statement without
specifying a type, as follows:
Dim myVar
If you don’t want to turn off the Strict option (which isn’t recommended, anyway), you can
declare the variable with the Object data type:
Dim myVar As Object
Every time your code references a new variable, Visual Basic will create an object variable. For
example, if the variable validKey hasn’t been declared, when Visual Basic runs into the following
line, it will create a new object variable and assign the value 002-6abbgd to it:
validKey = ”002-6abbgd”
You can use object variables in both numeric and string calculations. Suppose that the variable
modemSpeed has been declared as Object with one of the following statements:
Dim modemSpeed ’ with Option Strict = Off
Dim modemSpeed As Object ’ with Option Strict = OnPetroutsos c02.tex V2 - 01/28/2008 12:12pm Page 59
VARIABLES 59
and later in your code you assign the following value to it:
modemSpeed = ”28.8”
The modemSpeed variable is a string variable that you can use in statements such as the
following:
MsgBox ”We suggest a ” & modemSpeed & ” modem.”
This statement displays the following message:
”We suggest a 28.8 modem.”
You can also treat the modemSpeed variable as a numeric value with the following
statement:
Debug.WriteLine ”A ” & modemSpeed & ” modem can transfer ” &
modemSpeed * 1024 /8&” bytes per second.”
This statement displays the following message:
”A 28.8 modem can transfer 3686.4 bytes per second.”
The first instance of the modemSpeed variable in the preceding statement is treated
as a string because this is the variant’s type according to the assignment statement
(we assigned a string to it). The second instance, however, is treated as a number
(a single-precision number). Visual Basic converts it to a numeric value because it’s used
in a numeric calculation.
Another example of this behavior of variants can be seen in the following statements:
Dim I As Integer, S As String
I=10
S = ”11”
Debug.WriteLine(I + S)
Debug.WriteLine(I & S)
The first WriteLine statement will display the numeric value 21, whereas the second statement
will print the string 1011. The plus operator (+) tells VB to add two values. In doing so, VB must
convert the two strings into numeric values and then add them. The concatenation operator (&)
tells VB to concatenate the two strings.
Visual Basic knows how to handle object variables in a way that makes sense. The result may
not be what you had in mind, but it certainly is dictated by common sense. If you really want to
concatenate the strings 10 and 11, you should use the & operator, which would tell Visual Basic
exactly what to do. Quite impressive, but for many programmers, this is a strange behavior that
can lead to subtle errors — and they avoid it. It’s up to you to decide whether to use variants and
how far you will go with them. Sure, you can perform tricks with variants, but you shouldn’t
overuse them to the point that others can’t read your code.Petroutsos c02.tex V2 - 01/28/2008 12:12pm Page 60
60 CHAPTER 2 VARIABLES AND DATA TYPES
VariablesasObjects
Variables in VB 2008 are more than just names or placeholders for values. They’re intelligent
entities that can not only store but also process their values. I don’t mean to scare you, but I think
you should be told: VB 2008 variables are objects. And here’s why: A variable that holds dates is
declared as such with the following statement:
Dim expiration As Date
Then you can assign a date value to the expiration variable with a statement like this:
expiration = #1/1/2003#
So far, nothing out of the ordinary; this is how you use variables with any other language. In
addition to holding a date, however, the expiration variable can manipulate dates. The following
expression will return a new date that’s three years ahead of the date stored in the expiration
variable:
expiration.AddYears(3)
The new date can be assigned to another date variable:
Dim newExpiration As Date
newExpiration = expiration.AddYears(3)
AddYears is a method that knows how to add a number of years to a Date variable. There are
similarly named methods for adding months, days, and so on. In addition to methods, the Date
type exposes properties, such as the Month and Day properties, which return the date’s month
and day number, respectively. The keywords following the period after the variable’s name are
called methods and properties, just like the properties and methods of the controls you place on a
form to create your application’s visual interface. The methods and properties (or the members)
of a variable expose the functionality that’s built into the class representing the variable itself.
Without this built-in functionality, you’d have to write some serious code to extract the month
from a date variable, to add a number of days to a given date, to figure out whether a character is
a letter, a digit, or a punctuation symbol, and so on. Much of the functionality that you’ll need in
an application that manipulates dates, numbers, or text has already been built into the variables
themselves.
Don’t let the terminology scare you. Think of variables as placeholders for values and access
their functionality with expressions like the ones shown earlier. Start using variables to store
values and, if you need to process them, enter a variable’s name followed by a period to see a list
of the members it exposes. In most cases, you’ll be able to figure out what these members do by
just reading their names. I’ll come back to the concept of variables as objects, but I wanted to hit it
right off the bat. A more detailed discussion of the notion of variables as objects can be found in
Chapter 11, ‘‘Working with Objects,’’ which discusses objects in detail.
Programming languages can treat simple variables much more efficiently than objects. An
integer takes two bytes in memory, and the compiler will generate very efficient code to
manipulate an integer variable (add it to another numeric value, compare it to another integer,
and so on). If you declare an integer variable and use it in your code as such, Visual Studio doesn’t
create an object to represent this value. It creates a new variable for storing integers, like good oldPetroutsos c02.tex V2 - 01/28/2008 12:12pm Page 61
VARIABLES AS OBJECTS 61
BASIC. After you call one of the variable’s methods, the compiler emits code to create the actual
object. This process is called boxing and it introduces a small delay, which is truly insignificant
compared to the convenience of manipulating a variable through its methods.
As you’ve seen by now, variables are objects. This shouldn’t come as a surprise, but it’s an odd
concept for programmerswith no experience in object-oriented programming.We haven’t covered
objects and classes formally yet, but you have a good idea of what an object is. It’s an entity that
exposes some functionality by means of properties and methods. The TextBox control is an object
and it exposes the Text property, which allows you to read or set the text on the control. Any
name followed by a period and another name signifies an object. The ‘‘other name’’ is a property
or method of the object.
Converting Variable Types
In many situations, you will need to convert variables from one type into another. Table 2.4 shows
the methods of the Convert class that perform data-type conversions.
Table 2.4: The Data-Type Conversion Methods of the Convert Class
Method Converts Its Argument To
ToBoolean Boolean
ToByte Byte
ToChar Unicode character
ToDateTime Date
ToDecimal Decimal
ToDouble Double
ToInt16 Short Integer (2-byte integer, Int16)
ToInt32 Integer (4-byte integer, Int32)
ToInt64 Long (8-byte integer, Int64)
ToSByte Signed Byte
CShort Short (2-byte integer, Int16)
ToSingle Single
ToString String
ToUInt16 Unsigned Integer (2-byte integer, Int16)
ToUInt32 Unsigned Integer (4-byte integer, Int32)
ToUInt64 Unsigned Long (8-byte integer, Int64)Petroutsos c02.tex V2 - 01/28/2008 12:12pm Page 62
62 CHAPTER 2 VARIABLES AND DATA TYPES
In addition to the methods of the Convert class, you can still use the data-conversion functions
of VB (CInt() to convert a numeric value to an Integer, CDbl() to convert a numeric value to
aDouble, CSng() to convert a numeric value to a Single, and so on), which you can look up in
the documentation. If you’re writing new applications in VB 2008, use the new Convert class to
convert between data types.
To convert the variable initialized as the following
Dim A As Integer
to a Double, use the ToDouble method of the Convert class:
Dim B As Double
B = Convert.ToDouble(A)
Suppose that you have declared two integers, as follows:
Dim A As Integer, B As Integer
A=23
B=7
The result of the operation A/B will be a Double value. The following statement
Debug.Write(A / B)
displays the value 3.28571428571429. The result is a Double value, which provides the
greatest possible accuracy. If you attempt to assign the result to a variable that hasn’t
been declared as Double, and the Strict option is on, then VB 2008 will generate an error
message. No other data type can accept this value without loss of accuracy. To store the
result to a Single variable, you must convert it explicitly with a statement like the
following:
Convert.ToSingle(A / B)
You can also use the DirectCast() function to convert a variable or expression from
one type to another. The DirectCast() function is identical to the CType() function.
Let’s say the variable A has been declared as String and holds the value 34.56. The
following statement converts the value of the A variable to a Decimal value and uses it
in a calculation:
Dim A As String = ”34.56”
Dim B As Double
B = DirectCast(A, Double) / 1.14
The conversion is necessary only if the Strict option is on, but it’s a good practice to perform
your conversions explicitly. The following section explains what might happen if your code relies
on implicit conversions.Petroutsos c02.tex V2 - 01/28/2008 12:12pm Page 63
VARIABLES AS OBJECTS 63
Widening and Narrowing Conversions
In some situations, VB 2008 will convert data types automatically, but not always. Let’s say
you have declared and initialized two variables, an Integer and a Double, with the following
statements:
Dim count As Integer = 99
Dim pi As Double = 3.1415926535897931
If the Strict option is off and you assign the variable pi to the count variable, the count
variable’s new value will be 3. (The Double value was rounded to an Integer value, according
to the variable’s type.) Although this may be what you want, in most cases it’s an oversight that
will lead to incorrect results.
If the Strict option is on and you attempt to perform the same assignment, the compiler will
generate an error message to the effect that you can’t convert a Double to an Integer. The exact
message is Option Strict disallows implicit conversions from Double to Integer.
When the Strict option is on, VB 2008 will perform conversions that do not result in loss of
accuracy (precision) or magnitude. These conversions are called widening conversions.Whenyou
assign an Integer value to a Double variable, no accuracy or magnitude is lost. This is a widening
conversion, because it goes from a narrower to a wider type.
On the other hand, when you assign a Double value to an Integer variable, some accuracy is
lost (the decimal digits must be truncated). This is a narrowing conversion,becausewegofroma
data type that can represent a wider range of values to a data type that can represent a narrower
range of values.
Because you, the programmer, are in control, you might want to give up the accuracy —
presumably, it’s no longer needed. Table 2.5 summarizes the widening conversions that VB 2008
will perform for you automatically.
Table 2.5: VB 2008 Widening Conversions
Original Data Type Wider Data Type
Any type Object
Byte Short, Integer, Long, Decimal, Single, Double
Short Integer, Long, Decimal, Single, Double
Integer Long, Decimal, Single, Double
Long Decimal, Single, Double
Decimal Single, Double
Single Double
Double None
Char StringPetroutsos c02.tex V2 - 01/28/2008 12:12pm Page 64
64 CHAPTER 2 VARIABLES AND DATA TYPES
If the Strict option is on, the compiler will point out all the statements that may cause runtime
errors, and you can reevaluate your choice of variable types. You can also turn on the Strict option
temporarily to see the compiler’s warnings, and then turn it off again.
Formatting Numbers
So far, you’ve seen how to use the basic data types of the CLR. All data types expose a ToString
method, which returns the variable’s value (a number or date) as a string, so that it can be used
with other strings in your code. The ToString method formats numbers and dates in many
ways and it’s probably one of the most commonly needed methods. You can call the ToString
method without any arguments, as we have done so far, to convert any value to a string. The
ToString method, however, accepts an optional argument, which determines how the value will
be formatted as a string. For example, you can format a number as currency by prefixing it with
the appropriate sign (for example, the dollar symbol) and displaying it to two decimal digits,
and you can display dates in many formats. Some reports require that negative amounts are
enclosed in parentheses. The ToString method allows you to display numbers and dates in any
way you wish.
Notice that ToString is a method, not a property. It returns a value that you can assign to a
string variable or pass as arguments to a function such as MsgBox(), but the original value is not
affected. The ToString method can also format a value if called with an optional argument:
ToString(formatString)
The formatString argument is a format specifier (a string that specifies the exact format
to be applied to the variable). This argument can be a specific character that corresponds to a
predetermined format (a standard format string, as it’s called) or a string of characters that
have special meaning in formatting numeric values (a picture format string). Use standard
format strings for the most common formatting options, and use picture strings to specify unusual
formatting requirements. To format the value 9959.95 as a dollar amount, you can use the
following standard currency:
Dim Amnt As Single = 9959.95
Dim strAmnt As String
strAmnt = Amnt.ToString(”C”)
Or use the following picture numeric format string:
strAmnt = Amnt.ToString(”$#,###.00”)
Both statements will format the value as $9,959.95. The ”C” argument in the first example
means currency and formats the numeric value as currency. If you’re using a non-U.S. version of
Windows, the currency symbol will change accordingly. Use the Regional And Language Options
tool in the Control Panel to temporarily change the current culture to a European one, and the
amount will be formatted with the Euro sign.
The picture format string is made up of literals and characters that have special meaning
in formatting. The dollar sign has no special meaning and will appear as is. The # symbol is a
digit placeholder; all # symbols will be replaced by numeric digits, starting from the right. If the
number has fewer digits than specified in the string, the extra symbols to the left will be ignored.Petroutsos c02.tex V2 - 01/28/2008 12:12pm Page 65
VARIABLES AS OBJECTS 65
The comma tells the Format function to insert a comma between thousands. The period is the
decimal point, which is followed by two more digit placeholders. Unlike the # sign, the 0
is a special placeholder: If there are not enough digits in the number for all the zeros you’ve
specified, a 0 will appear in the place of the missing digits. If the original value had been 9959.9,
for example, the last statement would have formatted it as $9,959.90. If you used the # placeholder
instead, the string returned by the Format method would have a single decimal digit.
Standard Numeric Format Strings
The ToString method of the numeric data types recognizes the standard numeric format strings
shown in Table 2.6.
Table 2.6: Standard Numeric Format Strings
Format Character Description Example
C or c Currency (12345.67).ToString(”C”) returns $12,345.67
D or d Decimal (123456789).ToString(”D”) returns 123456789.
It works with integer values only.
E or e Scientific format (12345.67).ToString(”E”) returns 1.234567E + 004
F or f Fixed-point format (12345.67).ToString(”F”) returns 12345.67
G or g General format Returns a value either in fixed-point or scientific format
N or n Number format (12345.67).ToString(”N”) returns 12,345.67
P or p Percentage (0.12345).ToString(”N”) returns 12,35%
R or r Round-trip (1 / 3).ToString(”R”)returns 0.33333333333333331
(where the G specifier would return a value with fewer
decimal digits: 0.333333333333333
X or x Hexadecimal format 250.ToString(”X”) returns FA
The format character can be followed by an integer. If present, the integer value specifies the
number of decimal places that are displayed. The default accuracy is two decimal digits.
The C format string causes the ToString method to return a string representing the num-
ber as a currency value. An integer following the C determines the number of decimal digits
that are displayed. If no number is provided, two digits are shown after the decimal separator.
Assuming that the variable value has been declared as Decimal and its value is 5596, then the
expression value.ToString(”C”) will return the string $5,596.00.Ifthevalueofthevariable
were 5596.4499, then the expression value.ToString(”C3”) would return the string $5,596.450.
Notice that not all format strings apply to all data types. For example, only integer values can
be converted to hexadecimal format, and the D format string works with integer values only.
There are format strings and digits for dates too, and they’re discussed in Chapter 13, where I
will present the Date data type and related topics in detail.Petroutsos c02.tex V2 - 01/28/2008 12:12pm Page 66
66 CHAPTER 2 VARIABLES AND DATA TYPES
Picture Numeric Format Strings
If the format characters listed in Table 2.6 are not adequate for the control you need over the
appearance of numeric values, you can provide your own picture format strings. Picture
format strings contain special characters that allow you to format your values exactly as you
like. Table 2.7 lists the picture formatting characters.
Table 2.7: Picture Numeric Format Strings
Format Character Description Effect
0 Display zero placeholder Results in a nonsignificant zero if a number has
fewer digits than there are zeros in the format
# Display digit placeholder Replaces the symbol with only significant digits
. Decimal point Displays a period (.) character
, Group separator Separates number groups — for example, 1,000
% Percent notation Displays a % character
E + 0, E−0, e + 0, e−0 Exponent notation Formats the output of exponent notation
\ Literal character Used with traditional formatting sequences like
such as \n (newline)
‘‘’’ Literal string Displays any string within quotes or apostrophes
literally
; Section separator Specifies different output if the numeric value to
be formatted is positive, negative, or zero
The following statements will print the highlighted values:
Dim Amount As Decimal = 42492.45
Debug.WriteLine(Amount.ToString(”$#,###.00”))
$42,492.45
Amount = 0.2678
Debug.WriteLine(Amount.ToString(”0.000”))
0.268
Amount = -24.95
Debug.WriteLine(Amount.ToString(”$#,###.00;($#,###.00)”))
($24.95)
User-Defined Data Types
In the previous sections, we used variables to store individual values. As a matter of fact, most
programs store sets of data of different types. For example, a program for balancing your
checkbook must store several pieces of information for each check: the check’s number, amount,Petroutsos c02.tex V2 - 01/28/2008 12:12pm Page 67
VARIABLES AS OBJECTS 67
date, and so on. All these pieces of information are necessary to process the checks, and ideally,
they should be stored together.
You can create custom data types that are made up of multiple values using structures.AVB
structure allows you to combine multiple values of the basic data types and handle them as a
whole. For example, each check in a checkbook-balancing application is stored in a separate
structure (or record), as shown in Figure 2.3. When you recall a given check, you need all the
information stored in the structure.
Figure 2.3
Pictorial representation
of a structure
To define a structure in VB 2008, use the Structure statement, which has the following syntax:
Structure structureName
Dim variable1 As varType
Dim variable2 As varType
...
Dim variablen As varType
End Structure
varType can be any of the data types supported by the CLR. The Dim statement can be replaced
by the Private or Public access modifiers. For structures, Dim is equivalent to Public.
After this declaration, you have in essence created a new data type that you can use in your
application. structureName can be used anywhere you’d use any of the base types (Integers,
Doubles, and so on). You can declare variables of this type and manipulate them as you
manipulate all other variables (with a little extra typing). The declaration for the CheckRecord
structure shown in Figure 2.3 is as follows:
Structure CheckRecord
Dim CheckNumber As Integer
Dim CheckDate As Date
Dim CheckAmount As Single
Dim CheckPaidTo As String
End Structure
This declaration must appear outside any procedure; you can’t declare a Structure in a
subroutine or function. Once declared, The CheckRecord structure becomes a new data type for
your application.
To declare variables of this new type, use a statement such as this one:
Dim check1 As CheckRecord, check2 As CheckRecordPetroutsos c02.tex V2 - 01/28/2008 12:12pm Page 68
68 CHAPTER 2 VARIABLES AND DATA TYPES
To assign a value to one of these variables, you must separately assign a value to each one of its
components (they are called fields), which can be accessed by combining the name of the variable
and the name of a field, separated by a period, as follows:
check1.CheckNumber = 275
Actually, as soon as you type the period following the variable’s name, a list of all
members to the CheckRecord structure will appear, as shown in Figure 2.4. Notice that the
structure supports a few members on its own. You didn’t write any code for the Equals,
GetType,and ToString members, but they’re standard members of any Structure object, and
you can use them in your code. Both the GetType and ToString methods will return a string
like ProjectName.FormName + CheckRecord. You can provide your own implementation of the
ToString method, which will return a more meaningful string:
Public Overrides Function ToString() As String
Return ”CHECK # ” & CheckNumber & ” FOR ” &
CheckAmount.ToString(”C”)
End Function
Figure 2.4
Variables of custom
types expose their mem-
bers as properties.
As you understand, structures are a lot like objects that expose their fields as properties and
then expose a few members of their own. The following statements initialize a CheckRecord
variable:
check2.CheckNumber = 275
check2.CheckDate = #09/12/2008#
check2.CheckAmount = 104.25
check2.CheckPaidTo = ”Gas Co.”Petroutsos c02.tex V2 - 01/28/2008 12:12pm Page 69
VARIABLES AS OBJECTS 69
You can also create arrays of structures with a declaration such as the following (arrays are
discussed later in this chapter):
Dim Checks(100) As CheckRecord
Each element in this array is a CheckRecord structure and it holds all the fields of a given check.
To access the fields of the third element of the array, use the following notation:
Checks(2).CheckNumber = 275
Checks(2).CheckDate = #09/12/2008#
Checks(2).CheckAmount = 104.25
Checks(2).CheckPaidTo = ”Gas Co.”
The Nothing Value
The Nothing value is used with object variables and indicates a variable that has not been
initialized. If you want to disassociate an object variable from the object it represents,
set it to Nothing. The following statements create an object variable that references a brush, uses
it, and then releases it:
Dim brush As SolidBrush
brush = New SolidBrush(Color.Blue)
{ use brush object to draw with}
brush = Nothing
The first statement declares the brush variable.Atthispoint,the brush variable is Nothing.
The second statement initializes the brush variable with the appropriate constructor (the brush
is initialized to a specific color). After the execution of the second statement, the brush variable
actually represents an object you can draw with in blue. After using it to draw something, you can
release it by setting it to Nothing.
If you want to find out whether an object variable has been initialized, use the Is or IsNot
operators, as shown in the following example:
Dim myPen As Pen
{ more statements here}
If myPen Is Nothing Then
myPen = New Pen(Color.Red)
End If
The variable myPen is initialized with the New constructor only if it hasn’t been initialized
already. If you want to release the myPen variable later in your code, you can set it to Nothing
with the assignment operator. When you compare an object to Nothing, however, you can’t use
the equals operator; you must use the Is and IsNot operators.
Examining Variable Types
Besides setting the types of variables and the functions for converting between types,
Visual Basic provides the GetType method, which returns a string with the variable’s typePetroutsos c02.tex V2 - 01/28/2008 12:12pm Page 70
70 CHAPTER 2 VARIABLES AND DATA TYPES
(Int32, Decimal, and so on). Any variable exposes these methods automatically, and you can
call them like this:
Dim var As Double
Debug.WriteLine ”The variable’s type is ” & var.GetType.ToString
There’s also a GetType operator, which accepts as an argument a type and returns a Type
object for the specific data type. The GetType method and GetType operator are used mostly in If
structures, like the following one:
If var.GetType() Is GetType(Double) Then
{ code to handle a Double value}
End If
Notice that the code doesn’t reference data type names directly. Instead, it uses the value
returned by the GetType operator to retrieve the type of the class System.Double and then
compares this value to the variable’s type with the Is (or the IsNot)keyword.
Is It a Number, String, or Date?
Another set of Visual Basic functions returns variables’ data types, but not the exact type. They
return a True/False value indicating whether a variable holds a numeric value, a date or an array.
The following functions are used to validate user input, as well as data stored in files, before you
process them.
IsNumeric() Returns True if its argument is a number (Short, Integer, Long, Single,
Double, Decimal). Use this function to determine whether a variable holds a numeric value
before passing it to a procedure that expects a numeric value or before processing it as a
number. The following statements keep prompting the user with an InputBox for a numeric
value. The user must enter a numeric value or click the Cancel button to exit. As long as the
user enters non-numeric values, the Input box keeps popping up and prompting for a numeric
value:
Dim strAge as String = ””
Dim Age As Integer
While Not IsNumeric(strAge)
strAge = InputBox(”Please enter your age”)
End While
Age = Convert.ToInt16(strAge)
The variable strAge is initialized to a non-numeric value so that the While...End While loop
will be executed at least once.
IsDate() Returns True if its argument is a valid date (or time). The following expressions
return True because they all represent valid dates:
IsDate(#10/12/2010#)
IsDate(”10/12/2010”)
IsDate(”October 12, 2010”)Petroutsos c02.tex V2 - 01/28/2008 12:12pm Page 71
VARIABLES AS OBJECTS 71
If the date expression includes the day name, as in the following expression, the IsDate()
function will return False:
IsDate(”Sat. October 12, 2010”) ’ FALSE
IsArray() Returns True if its argument is an array.
Why Declare Variables?
Visual Basic never enforced variable declaration (and it still doesn’t), which was a good thing
for the beginner programmer. When you want to slap together a ‘‘quick-and-dirty’’ program,
the last thing you need is someone telling you to decide which variables you’re going to use
and to declare them before using them. This convenience, however, is a blessing in disguise
because most programmers accustomed to the free format of Visual Basic also carry their habits
of quick-and-dirty coding to large projects. When writing large applications, you will sooner or
later discover that variable declaration is a necessity. It will help you write clean, strongly typed
code and simplify debugging. Variable declaration eliminates the source of the most common and
totally unnecessary bugs.
Let’s examine the side effects of using undeclared variables in your application. To be able to
get by without declaring your variables, you must set the Explicit option to Off. Let’s assume that
you’re using the following statements to convert Euros to U.S. dollars:
Euro2USD = 1.462
USDollars = amount * Euro2USD
Thefirsttimeyourcodereferstothe Euro2USD variable name, Visual Basic creates a new
variable and then uses it as if it were declared.
Suppose that the variable Euro2USD appears in many places in your application. If in one
of these places you type Euro2UDS, and the program doesn’t enforce variable declaration,
the compiler will create a new variable, assign it the value zero, and then use it. Any amount
converted with the Euro2UDS variable will be zero! If the application enforces variable declaration,
the compiler will complain (the Euro2UDS variable hasn’t been declared), and you will catch the
error right in the editor, as you type.
AVariable’sScope
In addition to its type, a variable also has a scope. The scope (or visibility) of a variable is the section
of the application that can see and manipulate the variable. If a variable is declared within a
procedure, only the code in the specific procedure has access to that variable; this variable doesn’t
exist for the rest of the application. When the variable’s scope is limited to a procedure, it’s called
local.
Suppose that you’re coding the Click event of a button to calculate the sumof all even numbers
in the range 0 to 100. One possible implementation is shown in Listing 2.4.
Listing 2.4: Summing Even Numbers
Private Sub Button1 Click(ByVal sender As Object,
ByVal e As System.EventArguments)Petroutsos c02.tex V2 - 01/28/2008 12:12pm Page 72
72 CHAPTER 2 VARIABLES AND DATA TYPES
Handles Button1.Click
Dim i As Integer
Dim Sum As Integer
For i = 0 to 100 Step 2
Sum = Sum + i
Next
MsgBox ”The sum is ” & Sum.ToString
End Sub
The variables i and Sum are local to the Button1 Click() procedure. If you attempt to set
the value of the Sum variable from within another procedure, Visual Basic will complain that the
variable hasn’t been declared. (Or, if you have turned off the Explicit option, it will create another
Sum variable, initialize it to zero, and then use it. But this won’t affect the variable Sum in the
Button1 Click() subroutine.) The Sum variable is said to have procedure-level scope: It’s visible
within the procedure and invisible outside the procedure.
Sometimes, however, you’ll need to use a variable with a broader scope; a variable that’s
available to all procedures within the same file. This variable, which must be declared outside
any procedure, is said to have a module-level scope. In principle, you could declare all variables
outside the procedures that use them, but this would lead to problems. Every procedure in the file
would have access to any variable, and you would need to be extremely careful not to change the
value of a variable without good reason. Variables that are needed by a single procedure (such as
loop counters) should be declared in that procedure.
Another type of scope is the block-level scope. Variables introduced in a block of code,
such as an If statement or a loop, are local to the block but invisible outside the block.
Let’s revise the previous code segment so that it calculates the sum of squares. To carry out
the calculation, we first compute the square of each value and then sum the squares. The square
of each value is stored to a variable that won’t be used outside the loop, so we can define
the sqrValue variable in the loop’s block and make it local to this specific loop, as shown in
Listing 2.5.
Listing 2.5: AVariableScopedinItsOwnBlock
Private Sub Button1 Click(ByVal sender As Object,
ByVal e As System.EventArguments)
Handles Button1.Click
Dim i, Sum As Integer
For i = 0 to 100 Step 2
Dim sqrValue As Integer
sqrValue =i*i
Sum = Sum + sqrValue
Next
MsgBox ”The sum of the squares is ” & Sum
End Sub
The sqrValue variable is not visible outside the block of the For...Next loop. If you attempt to
use it before the For statement or after the Next statement, VB will throw an exception.Petroutsos c02.tex V2 - 01/28/2008 12:12pm Page 73
VARIABLES AS OBJECTS 73
The sqrValue variable maintains its value between iterations. The block-level variable is not
initialized at each iteration, even though there’s a Dim statement in the loop.
Finally, in some situations, the entire application must access a certain variable. In this case,
the variable must be declared as Public. Public variables have a global scope: They are visible from
any part of the application. To declare a public variable, use the Public statement in place of the
Dim statement. Moreover, you can’t declare public variables in a procedure. If you have multiple
forms in your application and you want the code in one form to see a certain variable in another
form, you can use the Public modifier.
The Public keyword makes the variable available not only to the entire project, but also to
all projects that reference the current project. If you want your variables to be public within a
project (in other words, available to all procedures in any module in the project) but invisible
to referencing projects, use the Friend keyword in the declaration of the module. Variables you
want to use throughout your project, but to not become available to other projects that reference
this one, should be declared as Friend.
So, why do we need so many types of scope? You’ll develop a better understanding of
scope and which type of scope to use for each variable as you get involved in larger projects.
In general, you should try to limit the scope of your variables as much as possible. If all variables
were declared within procedures, you could use the same name for storing a temporary value
in each procedure and be sure that one procedure’s variables wouldn’t interfere with those of
another procedure, even if you use the same name.
A Variable’s Lifetime
In addition to type and scope, variables have a lifetime, which is the period for which they retain
their value. Variables declared as Public exist for the lifetime of the application. Local variables,
declared within procedures with the Dim or Private statement, live as long as the procedure.
When the procedure finishes, the local variables cease to exist, and the allocated memory is
returned to the system. Of course, the same procedure can be called again. In this case, the local
variables are re-created and initialized again. If a procedure calls another, its local variables retain
their values while the called procedure is running.
You also can force a local variable to preserve its value between procedure calls by using the
Static keyword. Suppose that the user of your application can enter numeric values at any time.
One of the tasks performed by the application is to track the average of the numeric values. Instead
of adding all the values each time the user adds a new value and dividing by the count, you can
keep a running total with the function RunningAvg(), which is shown in Listing 2.6.
Listing 2.6: Calculations with Global Variables
Function RunningAvg(ByVal newValue As Double) As Double
CurrentTotal = CurrentTotal + newValue
TotalItems = TotalItems + 1
RunningAvg = CurrentTotal / TotalItems
End Function
You must declare the variables CurrentTotal and TotalItems outside the function so that
their values are preserved between calls. Alternatively, you can declare them in the function with
the Static keyword, as shown in Listing 2.7.Petroutsos c02.tex V2 - 01/28/2008 12:12pm Page 74
74 CHAPTER 2 VARIABLES AND DATA TYPES
Listing 2.7: Calculations with Local Static Variables
Function RunningAvg(ByVal newValue As Double) As Double
Static CurrentTotal As Double
Static TotalItems As Integer
CurrentTotal = CurrentTotal + newValue
TotalItems = TotalItems + 1
RunningAvg = CurrentTotal / TotalItems
End Function
The advantage of using static variables is that they help you minimize the number of total
variables in the application. All you need is the running average, which the RunningAvg()
function provides without making its variables visible to the rest of the application. Therefore,
you don’t risk changing the variables’ values from within other procedures.
Variables declared in a module outside any procedure take effect when the form is loaded and
cease to exist when the form is unloaded. If the form is loaded again, its variables are initialized as
if it’s being loaded for the first time.
Variables are initialized when they’re declared, according to their type. Numeric variables
are initialized to zero, string variables are initialized to a blank string, and object variables are
initialized to Nothing.
Constants
Some variables don’t change value during the execution of a program. These variables are
constants that appear many times in your code. For instance, if your program does math
calculations, the value of pi (3.14159...) might appear many times. Instead of typing the value
3.14159 over and over again, you can define a constant, name it pi, and use the name of the
constant in your code. The statement
circumference =2*pi* radius
is much easier to understand than the equivalent
circumference = 2 * 3.14159 * radius
You could declare pi as a variable, but constants are preferred for two reasons:
Constants don’t change value. This is a safety feature. After a constant has been declared,
you can’t change its value in subsequent statements, so you can be sure that the value specified
in the constant’s declaration will take effect in the entire program.
Constants are processed faster than variables. When the program is running, the values
of constants don’t have to be looked up. The compiler substitutes constant names with their
values, and the program executes faster.Petroutsos c02.tex V2 - 01/28/2008 12:12pm Page 75
ARRAYS 75
The manner in which you declare constants is similar to the manner in which you declare
variables, except that you use the Const keyword and in addition to supplying the constant’s
name, you must also supply a value, as follows:
Const constantname As type = value
Constants also have a scope and can be Public or Private. The constant pi, for instance, is
usually declared in a module as Public so that every procedure can access it:
Public Const pi As Double = 3.14159265358979
The name of the constant follows the same rules as variable names. The constant’s value is a
literal value or a simple expression composed of numeric or string constants and operators. You
can’t use functions in declaring constants. By the way, the specific value I used for this example
need not be stored in a constant. Use the pi member of the Math class instead (Math.pi).
Constants can be strings, too, like these:
Const ExpDate = #31/12/1997#
Const ValidKey = ”A567dfe”
Arrays
A standard structure for storing data in any programming language is the array. Whereas
individual variables can hold single entities, such as one number, one date, or one string, arrays
can hold sets of data of the same type (a set of numbers, a series of dates, and so on). An array has
a name, as does a variable, and the values stored in it can be accessed by an index.
For example, you could use the variable Salary to store a person’s salary:
Salary = 34000
But what if you wanted to store the salaries of 16 employees? You could either declare 16
variables — Salary1, Salary2,andsoonupto Salary16 — or declare an array with 16 elements.
An array is similar to a variable: It has a name and multiple values. Each value
is identified by an index (an integer value) that follows the array’s name in parentheses. Each
different value is an element of the array. If the array Salaries holds the salaries of 16 employ-
ees, the element Salaries(0) holds the salary of the first employee, the element Salaries(1)
holds the salary of the second employee, and so on up to the element Salaries(15).
Declaring Arrays
Unlike simple variables, arrays must be declared with the Dim (or Public) statement followed by
the name of the array and the index of the last element in the array in parentheses— for example:
Dim Salary(15) As IntegerPetroutsos c02.tex V2 - 01/28/2008 12:12pm Page 76
76 CHAPTER 2 VARIABLES AND DATA TYPES
Salary is the name of an array that holds 16 values (the salaries of the 16 employees)
with indices ranging from 0 to 15. Salary(0) is the first person’s salary, Salary(1) the second
person’s salary, and so on. All you have to do is remember who corresponds to each salary, but
even this data can be handled by another array. To do this, you’d declare another array of 16
elements:
Dim Names(15) As String
Then assign values to the elements of both arrays:
Names(0) = ”Joe Doe”
Salary(0) = 34000
Names(1) = ”Beth York”
Salary(1) = 62000
...
Names(15) = ”Peter Smack”
Salary(15) = 10300
This structure is more compact and more convenient than having to hard-code the names of
employees and their salaries in variables.
All elements in an array have the same data type. Of course, when the data type is Object,
the individual elements can contain different kinds of data (objects, strings, numbers, and
so on).
Arrays, like variables, are not limited to the basic data types. You can declare arrays that hold
any type of data, including objects. The following array holds colors, which can be used later in
the code as arguments to the various functions that draw shapes:
Dim colors(2) As Color
colors(0) = Color.BurlyWood
colors(1) = Color.AliceBlue
colors(2) = Color.Sienna
The Color class represents colors, and among the properties it exposes are the names of the
colors it recognizes.
A better technique for storing names and salaries is to create a structure and then declare an
array of this type. The following structure holds names and salaries:
Structure Employee
Dim Name As String
Dim Salary As Decimal
End Structure
Insert this declaration in a form’s code file, outside any procedure. Then create an array of the
Employee type:
Dim Emps(15) As EmployeePetroutsos c02.tex V2 - 01/28/2008 12:12pm Page 77
ARRAYS 77
Each element in the Emps array exposes two fields, and you can assign values to them by using
statements such as the following:
Emps(2).Name = ”Beth York”
Emps(2).Salary = 62000
The advantage of using an array of structures instead of multiple arrays is that the related
information will always be located under the same index. The code is more compact, and you
need not maintain multiple arrays.
Initializing Arrays
Just as you can initialize variables in the same line in which you declare them, you can initialize
arrays, too, with the following constructor (an array initializer, as it’s called):
Dim arrayname() As type = {entry0, entry1, ... entryN}
Here’s an example that initializes an array of strings:
Dim Names() As String = {”Joe Doe”, ”Peter Smack”}
This statement is equivalent to the following statements, which declare an array with two
elements and then set their values:
Dim Names(1) As String
Names(0) = ”Joe Doe”
Names(1) = ”Peter Smack”
The number of elements in the curly brackets following the array’s declaration determines the
dimensions of the array, and you can’t add new elements to the array without resizing it. If you
need to resize the array in your code dynamically, you must use the ReDim statement, as described
in the section called ‘‘Dynamic Arrays,’’ later in this chapter. However, you can change the value
of the existing elements at will, as you would with any other array.
Array Limits
The first element of an array has index 0. The number that appears in parentheses in the Dim
statement is one fewer than the array’s total capacity and is the array’s upper limit (or upper
bound). The index of the last element of an array (its upper bound) is given by the method
GetUpperBound, which accepts as an argument the dimension of the array and returns the
upper bound for this dimension. The arrays we examined so far are one-dimensional and the
argument to be passed to the GetUpperBound method is the value 0. The total number of
elements in the array is given by the method GetLength, which also accepts a dimension
as an argument. The upper bound of the following array is 19, and the capacity of the array is
20 elements:
Dim Names(19) As IntegerPetroutsos c02.tex V2 - 01/28/2008 12:12pm Page 78
78 CHAPTER 2 VARIABLES AND DATA TYPES
The first element is Names(0), and the last is Names(19). If you execute the following
statements, the highlighted values will appear in the Output window:
Debug.WriteLine(Names.GetLowerBound(0))
0
Debug.WriteLine(Names.GetUpperBound(0))
19
To assign a value to the first and last element of the Names array, use the following
statements:
Names(0) = ”First entry”
Names(19) = ”Last entry”
If you want to iterate through the array’s elements, use a loop like the following one:
Dim i As Integer, myArray(19) As Integer
Fori=0To myArray.GetUpperBound(0)
myArray(i) = i * 1000
Next
The actual number of elements in an array is given by the expression
myArray.GetUpperBound(0) + 1. You can also use the array’s Length property to retrieve the
count of elements. The following statement will print the number of elements in the array myArray
in the Output window:
Debug.WriteLine(myArray.Length)
Still confused with the zero-indexing scheme, the count of elements, and the index of the
last element in the array? You can make the array a little larger than it needs to be and
ignorethefirstelement.Justmakesurethat you never use the zero element in your
code — don’t store a value in the element Array(0), and you can then ignore this element.
To get 20 elements, declare an array with 21 elements as Dim MyArray(20) As type and
then ignore the first element.
Multidimensional Arrays
One-dimensional arrays, such as those presented so far, are good for storing long sequences of
one-dimensional data (such as names or temperatures). But how would you store a list of cities and
their average temperatures in an array? Or names and scores; years and profits; or data with more
than two dimensions, such as products, prices, and units in stock? In some situations, you will
want to store sequences of multidimensional data. You can store the same data more conveniently
in an array of as many dimensions as needed.
Figure 2.5 shows two one-dimensional arrays — one of them with city names, the other with
temperatures. The name of the third city would be City(2), and its temperature would be
Temperature(2).
A two-dimensional array has two indices: The first identifies the row (the order of the
city in the array), and the second identifies the column (city or temperature). To access the namePetroutsos c02.tex V2 - 01/28/2008 12:12pm Page 79
ARRAYS 79
and temperature of the third city in the two-dimensional array, use the following
indices:
Temperatures(2, 0) ’ is the third city’s name
Temperatures(2, 1) ’ is the third city’s average temperature
Figure 2.5
Two one-dimensional
arrays and the equiv-
alent two-dimensional
array
The benefit of using multidimensional arrays is that they’re conceptually easier to manage.
Suppose that you’re writing a game and want to track the positions of certain pieces on a board.
Each square on the board is identified by two numbers: its horizontal and vertical coordinates.
The obvious structure for tracking the board’s squares is a two-dimensional array, in which the
first index corresponds to the row number, and the second corresponds to the column number.
The array could be declared as follows:
Dim Board(9, 9) As Integer
When a piece is moved from the square in the first row and first column to the square in the
third row and fifth column, you assign the value 0 to the element that corresponds to the initial
position:
Board(0, 0) = 0
And you assign 1 to the square to which it was moved to indicate the new state of the
board:
Board(2, 4) = 1
To find out whether a piece is on the top-left square, you’d use the following statement:
If Board(0, 0) = 1 Then
{ piece found}
Else
{ empty square}
End IfPetroutsos c02.tex V2 - 01/28/2008 12:12pm Page 80
80 CHAPTER 2 VARIABLES AND DATA TYPES
This notation can be extended to more than two dimensions. The following statement creates
an array with 1,000 elements (10 by 10 by 10):
Dim Matrix(9, 9, 9)
You can think of a three-dimensional array as a cube made up of overlaid two-dimensional
arrays, such as the one shown in Figure 2.6.
Figure 2.6
Pictorial representa-
tions of one-, two-, and
three-dimensional arrays
It is possible to initialize a multidimensional array with a single statement, just as you do with
a one-dimensional array. You must insert enough commas in the parentheses following the array
name to indicate the array’s rank. The following statements initialize a two-dimensional array and
then print a couple of its elements:
Dim a(,) As Integer = {{10, 20, 30}, {11, 21, 31}, {12, 22, 32}}
Console.WriteLine(a(0, 1)) ’ will print 20
Console.WriteLine(a(2, 2)) ’ will print 32
You should break the line that initializes the dimensions of the array into multiple lines to
make your code easier to read. Just insert the line continuation character at the end of each
continued line:
Dim a(,) As Integer = {{10, 20, 30},
{11, 21, 31},
{12, 22, 32}}
If the array has more than one dimension, you can find out the number of dimensions with
the Array.Rank property. Let’s say you have declared an array for storing names and salaries by
using the following statements:
Dim Employees(1,99) As Employee
To find out the number of dimensions, use the following statement:
Employees.RankPetroutsos c02.tex V2 - 01/28/2008 12:12pm Page 81
ARRAYS 81
When using the Length property to find out the number of elements in a multidimensional
array, you will get back the total number of elements in the array (2 × 100 for our example). To
find out the number of elements in a specific dimension, use the GetLength method, passing as an
argument a specific dimension. The following expressions will return the number of elements in
the two dimensions of the array:
Debug.WriteLine(Employees.GetLength(0))
2
Debug.WriteLine(Employees.GetLength(1))
100
Because the index of the first array element is zero, the index of the last element is the length
of the array minus 1. Let’s say you have declared an array with the following statement to store
player statistics for 15 players, and there are five values per player:
Dim Statistics(14, 4) As Integer
The following statements will return the highlighted values shown beneath them:
Debug.WriteLine(Statistics.Rank)
2 ’ dimensions in array
Debug.WriteLine(Statistics.Length)
75 ’ total elements in array
Debug.WriteLine(Statistics.GetLength(0))
15 ’ elements in first dimension
Debug.WriteLine(Statistics.GetLength(1))
5 ’ elements in second dimension
Debug.WriteLine(Statistics.GetUpperBound(0))
14 ’ last index in the first dimension
Debug.WriteLine(Statistics.GetUpperBound(1))
4 ’ last index in the second dimension
Multidimensional arrays are becoming obsolete because arrays (and other collections) of
custom structures and objects are more flexible and convenient.
Dynamic Arrays
Sometimes you may not know how large to make an array. Instead of making it large enough
to hold the (anticipated) maximum number of data (which means that, on the average, part of
the array may be empty), you can declare a dynamic array. The size of a dynamic array can vary
during the course of the program. Or you might need an array until the user has entered a bunch
of data, and the application has processed it and displayed the results. Why keep all the data in
memory when it is no longer needed? With a dynamic array, you can discard the data and return
the resources it occupied to the system.
To create a dynamic array, declare it as usual with the Dim statement (or Public or Private),
but don’t specify its dimensions:
Dim DynArray() As IntegerPetroutsos c02.tex V2 - 01/28/2008 12:12pm Page 82
82 CHAPTER 2 VARIABLES AND DATA TYPES
Later in the program, when you know how many elements you want to store in the array, use
the ReDim statement to redimension the array, this time to its actual size. In the following example,
UserCount is a user-entered value:
ReDim DynArray(UserCount)
The ReDim statement can appear only in a procedure. Unlike the Dim statement, ReDim is
executable— it forces the application to carry out an action at runtime. Dim statements aren’t
executable, and they can appear outside procedures.
A dynamic array also can be redimensioned to multiple dimensions. Declare it with the Dim
statement outside any procedure, as follows:
Dim Matrix() As Double
Then use the ReDim statement in a procedure to declare a three-dimensional array:
ReDim Matrix(9, 9, 9)
Note that the ReDim statement can’t change the type of the array — that’s why the As clause
is missing from the ReDim statement. Moreover, subsequent ReDim statements can change the
bounds of the array Matrix but not the number of its dimensions. For example, you can’t use the
statement ReDim Matrix(99, 99) laterinyourcode.
The Preserve Keyword
Each time you execute the ReDim statement, all the values currently stored in the array are lost.
Visual Basic resets the values of the elements as if the array were just declared (it resets numeric
elements to zero and String elements to empty strings.) You can, however, change the size of the
array without losing its data. The ReDim statement recognizes the Preserve keyword, which forces
it to resize the array without discarding the existing data. For example, you can enlarge an array
by one element without losing the values of the existing elements:
ReDim Preserve DynamicArray(DynArray.GetUpperBound(0) + 1)
If the array DynamicArray held 12 elements, this statement would add one element to the array:
the element DynamicArray(12). The values of the elements with indices 0 through 11 wouldn’t
change.
The BottomLine
Declare and use variables. Programs use variables to store information during
their execution, and different types of information are stored in variables of different types.
Dates, for example, are stored in variables of the Date type, while text is stored in variables
of the String type. The various data types expose a lot of functionality that’s specific to a data
type; the methods provided by each data type are listed in the IntelliSense box.Petroutsos c02.tex V2 - 01/28/2008 12:12pm Page 83
THE BOTTOM LINE 83
Master It How would you declare and initialize a few variables?
Master It Explain briefly the Explicit, Strict, and Infer options.
Use the native data types. The CLR recognized the following data types, which you can
use in your code to declare variables: Strings, Numeric types, Date and time types, Boolean
data type.
All other variables, or variables that are declared without a type, are Object variables and can
store any data type, or any object.
Master It How will the compiler treat the following statement?
Dim amount = 32
Create custom data types. Practical applications need to store and manipulate multiple
data items, not just integers and strings. To maintain information about people, we
need to store each person’s name, date of birth, address, and so on. Products have a
name, a description, a price, and other related items. To represent such entities in our code,
we use structures, which hold many pieces of information about a specific entity
together.
Master It Create a structure for storing products and populate it with data.
Use arrays. Arrays are structures for storing sets of data, as opposed to single-valued
variables.
Master It How would you declare an array for storing 12 names and another one for
storing 100 names and Social Security numbers?Petroutsos c02.tex V2 - 01/28/2008 12:12pm Page 84Petroutsos c03.tex V2 - 01/28/2008 1:01pm Page 85
Chapter 3
Programming Fundamentals
The one thing you should have learned about programming in Visual Basic so far is that an appli-
cation is made up of small, self-contained segments. The code you write isn’t a monolithic listing;
it’s made up of small segments called procedures, and you work on one procedure at a time.
The two types of procedures supported by Visual Basic are the topics we’ll explore in this
chapter: subroutines and functions — the building blocks of your applications.We’ll discuss them
in detail: how to call them with arguments and how to retrieve the results returned by the func-
tions. You’ll learn how to use the built-in functions that come with the language, as well as how to
write your own subroutines and functions.
The statements that make up the core of the language are actually very few. The flexibility of
any programming language is based on its capacity to alter the sequence in which the statements
are executed through a set of so-called flow-control statements. These are the statements that
literally make decisions and react differently depending on the data, user actions, or external
conditions. Among other topics, in this chapter you’ll learn how to do the following:
◆ Use Visual Basic’s flow-control statements
◆ Write subroutines and functions
◆ Pass arguments to subroutines and functions
Flow-Control Statements
What makes programming languages so flexible and capable of handling every situation and pro-
gramming challenge with a relatively small set of commands is their capability to examine external
or internal conditions and act accordingly. Programs aren’tmonolithic sets of commands that carry
out the same calculations every time they are executed; this is what calculators (and extremely sim-
ple programs) do. Instead, they adjust their behavior depending on the data supplied; on external
conditions, such as a mouse click or the existence of a peripheral; even on abnormal conditions
generated by the program itself.
In effect, the statements discussed in the first half of this chapter are what programming is all
about. Without the capability to control the flow of the program, computers would just be bulky
calculators. You have seen how to use the If statement to alter the flow of execution in previous
chapters, and I assume you’re somewhat familiar with these kinds of statements. In this section,
you’ll find a formal discussion of flow-control statements. These statements are grouped into two
major categories: decision statements and looping statements.Petroutsos c03.tex V2 - 01/28/2008 1:01pm Page 86
86 CHAPTER 3 PROGRAMMING FUNDAMENTALS
Decision Statements
Applications need a mechanism to test conditions and take a different course of action depending
on the outcome of the test. Visual Basic provides three such decision,or conditional, statements:
◆ If...Then
◆ If...Then...Else
◆ Select Case
If...Then
The If...Then statement tests an expression, which is known as a condition. If the condition is
True, the program executes the statement(s) that follow. The If...Then statement can have a
single-line or a multiple-line syntax. To execute one statement conditionally, use the single-line
syntax as follows:
If condition Then statement
Conditions are logical expressions that evaluate to a True/False value and they usually contain
comparison operators— equals (=), different (<>), less than (<), greater than (>), less than or
equal to (<=), and so on — and logical operators: And, Or, Xor,and Not. Here are a few examples
of valid conditions:
If (age1 < age2) And (age1 > 12) Then ...
If score1 = score2 Then ...
The parentheses are not really needed in the first sample expression, but they make the code
a little easier to read. Sometimes parentheses are mandatory, to specify the order in which the
expression’s parts will be evaluated, just like math formulae may require parentheses to indicate
the precedence of calculations. You can also execute multiple statements by separating them
with colons:
If condition Then statement: statement: statement
Here’s an example of a single-line If statement:
expDate = expDate + 1
If expdate.Month > 12 Then expYear = expYear + 1: expMonth = 1
You can break this statement into multiple lines by using the multiline syntax of the If state-
ment, which delimits the statements to be executed conditionally with the End If statement, as
shown here:
If expDate.Month > 12 Then
expYear = expYear + 1
expMonth = 1
End If
The Month property of the Date type returns the month of the date to which it’s applied as a
numeric value. Most VB developers prefer the multiple-line syntax of the If statement, even ifPetroutsos c03.tex V2 - 01/28/2008 1:01pm Page 87
FLOW-CONTROL STATEMENTS 87
it contains a single statement. The block of statements between the Then and End If keywords
form the body of the conditional statement, and you can have as many statements in the body
as needed.
Many control properties are Boolean values, which evaluate to a True/False value. Let’s say
that your interface contains a CheckBox control and you want to set its caption to On or Off
depending on whether it’s selected at the time. Here’s an If statement that changes the caption of
the CheckBox:
If CheckBox1.Checked Then
CheckBox1.Text = ”ON”
Else
CheckBox1.Text = ”OFF”
End If
This statement changes the caption of the CheckBox all right, but when should it be executed?
Insert the statement in the CheckBox control’s CheckedChanged event handler, which is fired every
time the control’s check mark is turned on or off, whether because of a user action on the interface
or from within your code.
The expressions can get quite complicated. The following expression evaluates to True if the
date1 variable represents a date earlier than the year 2008 and either one of the score1 and score2
variables exceeds 90:
If (date1 < #1/1/2008) And (score1 < 90 Or score2 < 90) Then
‘ statements
End If
The parentheses around the last part of the comparison are mandatory, because we want the
compiler to perform the following comparison first:
score1 < 90 Or score2 < 90
If either variable exceeds 90, the preceding expression evaluates to True and the initial condi-
tion is reduced to the following:
If (date1 < #1/1/2008) And (True) Then
The compiler will evaluate the first part of the expression (it will compare two dates) and
finally it will combine two Boolean values with the And operator: if both values are True, the entire
condition is True; otherwise, it’s False. If you didn’t use parentheses, the compiler would evaluate
the three parts of the expression:
expression1: date1 < #1/1/2008#
expression2: score1 < 90
expression3: score2 < 90
Then it would combine expression1 with expression2 using the And operator, and finally it
would combine the result with expression3 using the OR operator. If score2 were less than 90,
the entire expression would evaluate to True, regardless of the value of the date1 variable.Petroutsos c03.tex V2 - 01/28/2008 1:01pm Page 88
88 CHAPTER 3 PROGRAMMING FUNDAMENTALS
If...Then...Else
A variation of the If...Then statement is the If...Then...Else statement, which executes one
block of statements if the condition is True and another block of statements if the condition is
False. The syntax of the If...Then...Else statement is as follows:
If condition Then
statementblock1
Else
statementblock2
End If
Visual Basic evaluates the condition; if it’s True, VB executes the first block of statements and
then jumps to the statement following the End If statement. If the condition is False, Visual Basic
ignores the first block of statements and executes the block following the Else keyword.
A third variation of the If...Then...Else statement uses several conditions, with the ElseIf
keyword:
If condition1 Then
statementblock1
ElseIf condition2 Then
statementblock2
ElseIf condition3 Then
statementblock3
Else
statementblock4
End If
You can have any number of ElseIf clauses. The conditions are evaluated from the top, and if
one of them is True, the corresponding block of statements is executed. The Else clause, which is
optional, will be executed if none of the previous expressions is True. Listing 3.1 is an example of
an If statement with ElseIf clauses.
Listing 3.1: Multiple ElseIf Statements
score = InputBox(”Enter score”)
If score < 50 Then
Result = ”Failed”
ElseIf score < 75 Then
Result = ”Pass”
ElseIf score < 90 Then
Result = ”Very Good”
Else
Result = ”Excellent”
End If
MsgBox ResultPetroutsos c03.tex V2 - 01/28/2008 1:01pm Page 89
FLOW-CONTROL STATEMENTS 89
Multiple If...Then Structures versus ElseIf
Notice that after a True condition is found, Visual Basic executes the associated statements and skips
the remaining clauses. It continues executing the program with the statement immediately after End
If. All following ElseIf clauses are skipped, and the code runs a bit faster. That’s why you should
prefer the complicated structure with the ElseIf statements used in Listing 3.1 to this equivalent
series of simple If statements:
If score < 50 Then
Result = ”Failed”
End If
If score < 75 And score >= 50 Then
Result = ”Pass”
End If
If score < 90 And score > =75 Then
Result = ”Very Good”
End If
If score >= 90 Then
Result = ”Excellent”
End If
With the multiple If statements, the compiler will generate code that evaluates all the conditions,
even if the score is less than 50.
The order of the comparisons is vital when you’re using multiple ElseIf statements. Had
you written the previous code segment with the first two conditions switched, like the following
segment, the results would be quite unexpected:
If score < 75 Then
Result = ”Pass”
ElseIf score < 50 Then
Result = ”Failed”
ElseIf score < 90 Then
Result = ”Very Good”
Else
Result = ”Excellent”
End If
Let’s assume that score is 49. The code would compare the score variable to the value 75.
Because 49 is less than 75, it would assign the value Pass to the variable Result,andthenitwould
skip the remaining clauses. Thus, a student who scored 49 would have passed the test! So be
extremely careful and test your code thoroughly if it usesmultiple ElseIf clauses. Youmust either
make sure they’re listed in the proper order or use upper and lower limits, as in the preceding
sidebar.Petroutsos c03.tex V2 - 01/28/2008 1:01pm Page 90
90 CHAPTER 3 PROGRAMMING FUNDAMENTALS
The IIf() Function
Not to be confused with the If...Then statement, VB provides the IIf() function. This built-in func-
tion accepts as an argument an expression and two values, evaluates the expression, and returns the
first value if the expression is True, or the second value if the expression is False. The syntax of
the IIf() function is the following:
IIf(expression, TruePart, FalsePart)
The TruePart and FalsePart arguments are objects. (They can be integers, strings, or any built-in
or custom object.) The IIf() function is a more compact notation for simple If statements. Let’s
say you want to display one of the strings ‘‘Close’’ or ‘‘Far’’, depending on the value of the distance
variable. Instead of a multiline If statement, you can call the IIf() function as follows:
IIf(distance > 1000, ”Far”, ”Close”)
Another typical example of the IIf() function is in formatting negative values. It’s fairly common in
business applications to display negative amounts in parentheses. Use the IIf() statement to write
a short expression that formats negative and positive amounts differently, like the following one:
IIf(amount < 0, ”(” &
Math.Abs(amount).ToString(”#,###.00”) & ”)”,
amount.ToString(”#,###.00”))
The Abs method of the Math class returns the absolute value of a numeric value, and the string
argument of the ToString method determines that the amount should have two decimal digits.
Select Case
An alternative to the efficient but difficult-to-read code of the multiple ElseIf structure is the
Select Case structure, which compares the same expression to different values. The advantage of
the Select Case statement over multiple If...Then...ElseIf statements is that it makes the code
easier to read and maintain.
The Select Case structure evaluates a single expression at the top of the structure. The result of
the expression is then compared with several values; if it matches one of them, the corresponding
block of statements is executed. Here’s the syntax of the Select Case statement:
Select Case expression
Case value1
statementblock1
Case value2
statementblock2
.
.Petroutsos c03.tex V2 - 01/28/2008 1:01pm Page 91
FLOW-CONTROL STATEMENTS 91
.
Case Else
statementblockN
End Select
A practical example based on the Select Case statement is shown in Listing 3.2.
Listing 3.2: Using the Select Case Statement
Dim Message As String
Select Case Now.DayOfWeek
Case DayOfWeek.Monday
message = ”Have a nice week”
Case DayOfWeek.Friday
message = ”Have a nice weekend”
Case Else
message = ”Welcome back!”
End Select
MsgBox(message)
In the listing, the expression that’s evaluated at the beginning of the statement is the
Now.DayOfWeek method. This method returns a member of the DayOfWeek enumeration, and
you can use the names of these members in your code to make it easier to read. The value of
this expression is compared with the values that follow each Case keyword. If they match, the
block of statements up to the next Case keyword is executed, and the program skips to the state-
ment following the End Select statement. The block of the Case Else statement is optional,
and is executed if none of the previous cases matches the expression. The first two Case state-
ments take care of Fridays and Mondays, and the Case Else statement takes care of the
other days.
Some Case statements can be followed by multiple values, which are separated by commas.
Listing 3.3 is a revised version of the previous example.
Listing 3.3: A Select Case StatementwithMultiple Cases per Clause
Select Case Now.DayOfWeek
Case DayOfWeek.Monday
message = ”Have a nice week”
Case DayOfWeek.Tuesday, DayOfWeek.Wednesday, DayOfWeek.Thursday
message = ”Welcome back!”
Case DayOfWeek.Friday, DayOfWeek.Saturday, DayOfWeek.Sunday
message = ”Have a nice weekend!”
End Select
MsgBox(message)Petroutsos c03.tex V2 - 01/28/2008 1:01pm Page 92
92 CHAPTER 3 PROGRAMMING FUNDAMENTALS
Monday, weekends, and weekdays are handled separately by three Case statements. The
second Case statement handles multiple values (all workdays except for Monday and Friday).
Monday is handled by a separate Case statement. This structure doesn’t contain a Case Else
statement because all possible values are examined in the Case statements; the DayOfWeek method
can’t return another value.
The Case statements can get a little more complex. For example, you may want to distinguish
a case where the variable is larger (or smaller) than a value. To implement this logic, use the Is
keyword, as in the following code segment that distinguishes between the first and second half of
the month:
Select Now.Day
Case Is < 15
MsgBox(”It’s the first half of the month”)
Case Is >=15
MsgBox(”It’s the second half of the month”)
End Select
Short-Circuiting Expression Evaluation
A common pitfall of evaluating expressions with VB is to attempt to compare a Nothing value
to something. An object variable that hasn’t been set to a value can’t be used in calculations or
comparisons. Consider the following statements:
Dim B As SolidBrush
B = New SolidBrush(Color.Cyan)
If B.Color = Color.White Then
MsgBox(”Please select another brush color”)
End If
These statements create a SolidBrush object variable, the B variable, and then examine the brush
color and prohibit the user from drawing with a white brush. The second statement initializes the
brush to the cyan color. (Every shape drawn with this brush will appear in cyan.) If you attempt
to use the B variable without initializing it, a runtime exception will be thrown: the infamous
NullReferenceException. In our example, the exception will be thrown when the program gets
to the If statement, because the B variable has no value (it’s Nothing), and the code attempts to
compare it to something. Nothing values can’t be compared to anything. Comment out the second
statement by inserting a single quote in front of it and then execute the code to see what will
happen. Then restore the statement by removing the comment mark.
Let’s fix it by making sure that B is not Nothing:
If B IsNot Nothing And B.Color = Color.White Then
MsgBox(”Please select another brush color”)
End If
The If statement should compare the Color property of the B object, only if the B object is not
Nothing. But this isn’t the case. The AND operator evaluates all terms in the expression and then
combines their results (True or False values) to determine the value of the expression. If they’re
all True, the result is also True. However, it won’t skip the evaluation of some terms as soon as
it hits a False value. To avoid unnecessary comparisons, use the AndAlso operator. The AndAlsoPetroutsos c03.tex V2 - 01/28/2008 1:01pm Page 93
FLOW-CONTROL STATEMENTS 93
operator does what the And operator should have done in the first place: It stops evaluating the
remaining terms or the expression because they won’t affect the result. If one of its operands is
False, the entire expression will evaluate to False. In other words, if B is Nothing, there’s no reason
to compare its color; the entire expression will evaluate to False, regardless of the brush’s color.
Here’s how we use the AndAlso operator:
If B IsNot Nothing AndAlso B.Color = Color.White Then
MsgBox(”Please select another brush color”)
End If
The AndAlso operator is said to short-circuit the evaluation of the entire expression as soon as
it runs into a False value. As soon as one of the parts in an AndAlso operation turns out to be False,
the entire expression is False and there’s no need to evaluate the remaining terms.
There’s an equivalent operator for short-circuiting OR expressions: the OrElse operator. The
OrElse operator can speed the evaluation of logical expressions a little, but it’s not as important
as the AndAlso operator. Another good reason for short-circuiting expression evaluation is to
help performance. If the second term of an And expression takes longer to execute (it has to access
a remote database, for example), you can use the AndAlso operator to make sure that it’s not
executed when not needed.
Loop Statements
Loop statements allow you to execute one or more lines of code repetitively. Many tasks consist of
operations that must be repeated over and over again, and loop statements are an important part
of any programming language. Visual Basic supports the following loop statements:
◆ For...Next
◆ Do...Loop
◆ While...End While
For...Next
Unlike the other two loops, the For...Next loop requires that you know the number of times that
the statements in the loop will be executed. The For...Next loop has the following syntax:
For counter = start To end [Step increment]
statements
Next [counter]
The keywords in the square brackets are optional. The arguments counter, start, end,and
increment are all numeric. The loop is executed as many times as required for the counter to
reach (or exceed) the end value.
In executing a For...Next loop, Visual Basic does the following:
1. Sets counter equal to start.
2. Tests to see whether counter is greater than end. If so, it exits the loop without executing the
statements in the loop’s body, not even once. If increment is negative, Visual Basic tests to
see whether counter is less than end. If it is, it exits the loop.
3. Executes the statements in the block.Petroutsos c03.tex V2 - 01/28/2008 1:01pm Page 94
94 CHAPTER 3 PROGRAMMING FUNDAMENTALS
4. Increases counter by the amount specified with the increment argument, following the
Step keyword. If the increment argument isn’t specified, counter is increased by 1. If Step
is a negative value, counter is decreased accordingly.
5. Continues with step 3.
The For...Next loop in Listing 3.4 scans all the elements of the numeric array data and calcu-
lates their average.
Listing 3.4: Iterating an Array with a For...Next Loop
Dim i As Integer, total As Double
Fori=0To data.GetUpperBound(0)
total = total + data(i)
Next i
Debug.WriteLine (total / Data.Length)
The single most important thing to keep in mind when working with For...Next loopsisthat
the loop’s ending value is set at the beginning of the loop. Changing the value of the end variable
in the loop’s body won’t have any effect. For example, the following loop will be executed 10
times, not 100 times:
Dim endValue As Integer = 10
Dim i as Integer
Fori=0To endValue
endValue = 100
{ more statements }
Next i
You can, however, adjust the value of the counter from within the loop. The following is an
example of an endless (or infinite) loop:
Fori=0To10
Debug.WriteLine(i)
i=i-1
Next i
This loop never ends because the loop’s counter, in effect, is never increased. (If you try this,
press Ctrl + Break to interrupt the endless loop.)
Do Not Manipulate the Loop’s Counter
Manipulating the counter of a For...Next loop is strongly discouraged. This practice willmost likely
lead to bugs such as infinite loops, overflows, and so on. If the number of repetitions of a loop isn’t
known in advance, use a Do...Loop or a While...End While structure (discussed in the following
section). To jump out of a For...Next loop prematurely, use the Next For statement.Petroutsos c03.tex V2 - 01/28/2008 1:01pm Page 95
FLOW-CONTROL STATEMENTS 95
The increment argument can be either positive or negative. If start is greater than end,the
value of increment must be negative. If not, the loop’s body won’t be executed, not even once.
VB 2008 allows you to declare the counter in the For statement. The counter variable ceases to
exist when the program bails out of the loop:
For i As Integer = 1 to 10
Debug.WriteLine(i.ToString)
Next
Debug.WriteLine(i.ToString)
The i variable is used as the loop’s counter and it’s not visible outside the loop. The last state-
ment won’t even compile; the editor will underline it with a wiggly line and will generate the error
message Name ‘i’ is not declared.
Do...Loop
The Do...Loop executes a block of statements for as long as a condition is True, or until a condition
becomes True. Visual Basic evaluates an expression (the loop’s condition), and if it’s True, the
statements in the loop’s body are executed. The expression is evaluated either at the beginning
of the loop (before executing any statements) or at the end of the loop (the block statements are
executed at least once). If the expression is False, the program’s execution continues with the
statement following the loop.
There are two variations of the Do...Loop statement; both use the same basic model. A loop
can be executed either while the condition is True or until the condition becomes True. These
two variations use the keywords While and Until to specify for how long the statements will be
executed. To execute a block of statements while a condition is True, use the following syntax:
Do While condition
statement-block
Loop
To execute a block of statements until the condition becomes True, use the following syntax:
Do Until condition
statement-block
Loop
When Visual Basic executes these loops, it first evaluates condition.If condition is False,
a Do...While loop is skipped (the statements aren’t even executed once), but a Do...Until loop
is executed. When the Loop statement is reached, Visual Basic evaluates the expression again; it
repeats the statement block of the Do...While loop if the expression is True or repeats the state-
ments of the Do...Until loop if the expression is False. In short, the Do...While loop is executed
when the condition is True, and the Do...Until loop is executed when the condition is False.
A last variation of the Do...Loop statement allows you to evaluate the condition at the end of the
loop. Here’s the syntax of both loops, with the evaluation of the condition at the end of the loop:
Do
statement-block
Loop While conditionPetroutsos c03.tex V2 - 01/28/2008 1:01pm Page 96
96 CHAPTER 3 PROGRAMMING FUNDAMENTALS
Do
statement-block
Loop Until condition
As you can guess, the statements in the loop’s body are executed at least once, because no
testing takes place as the loop is entered.
Here’s a typical example of using a Do...Loop: Suppose that the variable MyText holds some
text (like the Text property of a TextBox control), and you want to count the words in the text.
(We’ll assume that there are no multiple spaces in the text and that the space character separates
successive words.) To locate an instance of a character in a string, use the IndexOf method, which
is discussed in detail in Chapter 13, ‘‘Handling Strings, Characters, and Dates.’’ This method
accepts two arguments: the starting location of the search and the character being searched.
The following loop repeats for as long as there are spaces in the text. Each time the IndexOf
method finds another space in the text, it returns the location of the space. When there are no
more spaces in the text, the IndexOf method returns the value –1, which signals the end of the
loop, as shown:
Dim MyText As String =
”The quick brown fox jumped over the lazy dog”
Dim position, words As Integer
position = 0: words = 0
Do While position >=0
position = MyText.IndexOf(” ”, position + 1)
words += 1
Loop
MsgBox(”There are ” & words & ” words in the text”)
The Do...Loop is executed while the IndexOf method function returns a positive number,
which means that there are more spaces (and therefore words) in the text. The variable position
holds the location of each successive space character in the text. The search for the next space starts
at the location of the current space plus 1 (so the program won’t keep finding the same space). For
each space found, the program increments the value of the words variable, which holds the total
number of words when the loop ends. By the way, there are simpler methods of breaking a string
into its constituent words, such as the Split method of the String class, which is discussed in
Chapter 13. This is just an example of the Do...While loop.
You might notice a problem with the previous code segment: It assumes that the text contains
at least one word. You should insert an If statement that detects zero-length strings and doesn’t
attempt to count words in them.
You can code the same routine with the Until keyword. In this case, you must continue search-
ing for spaces until position becomes –1. Here’s the same code with a different loop:
Dim position As Integer = 0
Dim words As Integer = 0
Do Until position = -1
position = MyText.IndexOf(” ”, position + 1)
words = words + 1
Loop
MsgBox(”There are ” & words & ” words in the text”)Petroutsos c03.tex V2 - 01/28/2008 1:01pm Page 97
FLOW-CONTROL STATEMENTS 97
While...End While
The While...End While loop executes a block of statements as long as a condition is True. The loop
has the following syntax:
While condition
statement-block
End While
If condition is True, all statements in the bock are executed. When the End While statement
is reached, control is returned to the While statement, which evaluates condition again. If con-
dition is still True, the process is repeated. If condition is False, the program resumes with the
statement following End While.
The loop in Listing 3.5 prompts the user for numeric data. The user can type a negative value
to indicate he’s done entering values and terminate the loop. As long as the user enters positive
numeric values, the program keeps adding them to the total variable.
Listing 3.5: Reading an Unknown Number of Values
Dim number, total As Double
number = 0
While number => 0
total = total + number
number = InputBox(”Please enter another value”)
End While
I’ve assigned the value 0 to the number variable before the loop starts because this value isn’t
negative and doesn’t affect the total.
Sometimes, the condition that determines when the loop will terminate can’t be evaluated at
the top of the loop. In these cases, we declare a Boolean value and set it to True or False from
within the loop’s body. Here’s the outline of such a loop:
Dim repeatLoop As Boolean
repeatLoop = True
While repeatLoop
{ statements }
If condition Then
repeatLoop = True
Else
repeattLoop = False
End If
End While
You may also see an odd loop statement like the following one:
While True
{ statements }
End WhilePetroutsos c03.tex V2 - 01/28/2008 1:01pm Page 98
98 CHAPTER 3 PROGRAMMING FUNDAMENTALS
It’s also common to express the True condition as follows:
While1=1
This seemingly endless loop must be terminated from within its own body with an Exit While
statement, which is called when a condition becomes True or False. The following loop terminates
when a condition is met in the loop’s body:
While True
{ statements }
If condition Then Exit While
{ more statements }
End While
Nested Control Structures
You can place, or nest, control structures inside other control structures (such as an If...Then
block within a For...Next loop). Control structures in Visual Basic can be nested in as many levels
as you want. The editor automatically indents the bodies of nested decision and loop structures to
make the program easier to read.
When you nest control structures, you must make sure that they open and close within the
same structure. In other words, you can’t start a For...Next loop in an If statement and close
the loop after the corresponding End If. The following code segment demonstrates how to nest
several flow-control statements. (The curly brackets denote that regular statements should appear
in their place and will not compile, of course.)
Fora=1To100
{ statements }
Ifa=99 Then
{ statements }
End If
While b < a
{ statements }
If total <= 0 Then
{ statements }
End If
End While
Forc=1toa
{ statements }
Next c
Next a
I’m showing the names of the counter variables after the Next statements to make the code
more readable. To find the matching closing statement (Next, End If,or End While), move down
from the opening statement until you hit a line that starts at the same column. This is the matching
closing statement. Notice that you don’t have to align the nested structures yourself; the editor
reformats the code automatically as you edit. It also inserts the matching closing statement— the
End If statement is inserted automatically as soon as you enter an If statement, for example.Petroutsos c03.tex V2 - 01/28/2008 1:01pm Page 99
FLOW-CONTROL STATEMENTS 99
Listing 3.6 shows the structure of a nested For...Next loop that scans all the elements of a
two-dimensional array.
Listing 3.6: Iterating through a Two-Dimensional Array
Dim Array2D(6, 4) As Integer
Dim iRow, iCol As Integer
For iRow = 0 To Array2D.GetUpperBound(0)
For iCol = 0 To Array2D.GetUpperBound(1)
Array2D(iRow, iCol) = iRow * 100 + iCol
Debug.Write(iRow & ”, ” & iCol & ”=”&
Array2D(iRow, iCol) & ” ”)
Next iCol
Debug.WriteLine()
Next iRow
The outer loop (with the iRow counter) scans each row of the array. At each iteration, the inner
loop scans all the elements in the row specified by the counter of the outer loop (iRow). After
the inner loop completes, the counter of the outer loop is increased by one, and the inner loop is
executed again — this time to scan the elements of the next row. The loop’s body consists of two
statements that assign a value to the current array element and then print it in the Output window.
The current element at each iteration is Array2D(iRow, iCol).
You can also nest multiple If statements. The code in Listing 3.7 tests a user-supplied value to
determine whether it’s positive; if so, it determines whether the value exceeds a certain limit.
Listing 3.7: Simple Nested If Statements
Dim Income As Decimal
Income = Convert.ToDecimal(InputBox(”Enter your income”))
If Income > 0 Then
If Income > 12000 Then
MsgBox ”You will pay taxes this year”
Else
MsgBox ”You won’t pay any taxes this year”
End If
Else
MsgBox ”Bummer”
End If
The Income variable is first comparedwith zero. If it’s negative, the Else clause of the If...Then
statement is executed. If it’s positive, it’s compared with the value 12,000, and depending on
the outcome, a different message is displayed. The code segment shown here doesn’t perform
any extensive validations and assumes that the user won’t enter a string when prompted for
her income.Petroutsos c03.tex V2 - 01/28/2008 1:01pm Page 100
100 CHAPTER 3 PROGRAMMING FUNDAMENTALS
The Exit Statement
The Exit statement allows you to exit prematurely from a block of statements in a control struc-
ture, from a loop, or even from a procedure. Suppose that you have a For...Next loop that
calculates the square root of a series of numbers. Because the square root of negative numbers
can’t be calculated (the Math.Sqrt method will generate a runtime error), you might want to halt
the operation if the array contains an invalid value. To exit the loop prematurely, use the Exit For
statement as follows:
Fori=0To UBound(nArray)
If nArray(i) < 0 Then
MsgBox(”Can’t complete calculations” & vbCrLf &
”Item ” & i.ToString & ” is negative! ”
Exit For
End If
nArray(i) = Math.Sqrt(nArray(i))
Next
If a negative element is found in this loop, the program exits the loop and continues with the
statement following the Next statement.
There are similar Exit statements for the Do loop (Exit Do), the While loop (Exit While), the
Select statement (Exit Select), and for functions and subroutines (Exit Function and Exit
Sub). If the previous loop was part of a function, you might want to display an error and exit not
only the loop, but also the function itself by using the Exit Function statement.
Writing andUsing Procedures
The idea of breaking a large application into smaller, more manageable sections is not new to
computing. Few tasks, programming or otherwise, can bemanaged as a whole. The event handlers
are just one example of breaking a large application into smaller tasks.
For example, when you write code for a control’s Click event, you concentrate on the event
at hand — namely, how the program should react to the Click event. What happens when the
control is double-clicked or when another control is clicked is something you will worry about
later — in another control’s event handler. This divide-and-conquer approach isn’t unique to
programming events. It permeates the Visual Basic language, and even the longest applications are
written by breaking them into small, well-defined, easily managed tasks. Each task is performed
by a separate procedure that is written and tested separately fromthe others. Asmentioned earlier,
the two types of procedures supported by Visual Basic are subroutines and functions.
Subroutines usually perform actions and they don’t return any result. Functions, on the other
hand,performsomecalculationsandreturnavalue.Thisistheonlydifferencebetweensubrou-
tines and functions. Both subroutines and functions can accept arguments, which are values you
pass to the procedure when you call it. Usually, the arguments are the values on which the proce-
dure’s code acts. Arguments and the related keywords are discussed in detail in the ‘‘Arguments’’
section later in this chapter.
Subroutines
A subroutine is a block of statements that carries out a well-defined task. The block of state-
ments is placed within a set of Sub...End Sub statements and can be invoked by name.Petroutsos c03.tex V2 - 01/28/2008 1:01pm Page 101
WRITING AND USING PROCEDURES 101
The following subroutine displays the current date in a message box and can be called by its name,
ShowDate():
Sub ShowDate()
MsgBox(Now().ToShortDateString)
End Sub
Normally, the task performed by a subroutine is more complicated than this; but even this
simple subroutine is a block of code isolated from the rest of the application. The statements in a
subroutine are executed, and when the End Sub statement is reached, control returns to the calling
program. It’s possible to exit a subroutine prematurely by using the Exit Sub statement.
All variables declared within a subroutine are local to that subroutine. When the subroutine
exits, all variables declared in it cease to exist.
Most procedures also accept and act upon arguments. The ShowDate() subroutine displays the
current date in a message box. If you want to display any other date, you have to implement it
differently and add an argument to the subroutine:
Sub ShowDate(ByVal birthDate As Date)
MsgBox(birthDate.ToShortDateString)
End Sub
birthDate is a variable that holds the date to be displayed; its type is Date. The ByVal keyword
means that the subroutine sees a copy of the variable, not the variable itself. What this means
practically is that the subroutine can’t change the value of the variable passed by the calling appli-
cation. To display the current date in a message box, you must call the ShowDate() subroutine as
follows from within your program:
ShowDate()
To display any other date with the second implementation of the subroutine, use a statement
like the following:
Dim myBirthDate = #2/9/1960#
ShowDate(myBirthDate)
Or, you can pass the value to be displayed directly without the use of an intermediate variable:
ShowDate(#2/9/1960#)
If you later decide to change the format of the date, there’s only one place in your code you
must edit: the statement that displays the date from within the ShowDate() subroutine.
Functions
A function is similar to a subroutine, but a function returns a result. Because they return values,
functions — like variables — have types. The value you pass back to the calling program from
a function is called the return value, and its type must match the type of the function. FunctionsPetroutsos c03.tex V2 - 01/28/2008 1:01pm Page 102
102 CHAPTER 3 PROGRAMMING FUNDAMENTALS
accept arguments, just like subroutines. The statements that make up a function are placed in a set
of Function...End Function statements, as shown here:
Function NextDay() As Date
Dim theNextDay As Date
theNextDay = Now.AddDays(1)
Return theNextDay
End Function
The Function keyword is followed by the function name and the As keyword that specifies its
type, similar to a variable declaration. AddDays is a method of the Date type, and it adds a number
of days to a Date value. The NextDay() function returns tomorrow’s date by adding one day to the
current date. NextDay() is a custom function, which calls the built-in AddDays method to complete
its calculations.
The result of a function is returned to the calling program with the Return statement, which
is followed by the value you want to return from your function. This value, which is usually a
variable, must be of the same type as the function. In our example, the Return statement happens
to be the last statement in the function, but it could appear anywhere; it could even appear several
times in the function’s code. The first time a Return statement is executed, the function terminates,
and control is returned to the calling program.
You can also return a value to the calling routine by assigning the result to the name of the
function. The following is an alternate method of coding the NextDay() function:
Function NextDay() As Date
NextDay = Now.AddDays(1)
End Function
Notice that this time I’ve assigned the result of the calculation to the function’s name directly
and didn’t use a variable. This assignment, however, doesn’t terminate the function like the
Return statement. It sets up the function’s return value, but the function will terminate when
the End Function statement is reached, or when an Exit Function statement is encountered.
Similar to variables, a custom function has a name that must be unique in its scope (which is
also true for subroutines, of course). If you declare a function in a form, the function name must be
unique in the form. If you declare a function as Public or Friend, its name must be unique in the
project. Functions have the same scope rules as variables and can be prefixed by many of the same
keywords. In effect, you can modify the default scope of a function with the keywords Public,
Private, Protected, Friend,and Protected Friend. In addition, functions have types, just like
variables, and they’re declared with the As keyword.
Suppose that the function CountWords() counts the number of words, and the function
CountChars() counts the number of characters in a string. The average length of a word could
be calculated as follows:
Dim longString As String, avgLen As Double
longString = TextBox1.Text
avgLen = CountChars(longString) / CountWords(longString)
The first executable statement gets the text of a TextBox control and assigns it to a variable,
which is then used as an argument to the two functions.When the third statement executes, VisualPetroutsos c03.tex V2 - 01/28/2008 1:01pm Page 103
ARGUMENTS 103
Basic first calls the functions CountChars() and CountWords() with the specified arguments, and
then divides the results they return.
You can call functions in the same way that you call subroutines, but the result won’t be stored
anywhere. For example, the function Convert() might convert the text in a text box to uppercase
and return the number of characters it converts. Normally, you’d call this function as follows:
nChars = Convert()
If you don’t care about the return value — you only want to update the text on a TextBox
control — you would call the Convert() function with the following statement:
Convert()
Arguments
Subroutines and functions aren’t entirely isolated from the rest of the application. Most proce-
dures accept arguments from the calling program. Recall that an argument is a value you pass to
the procedure and on which the procedure usually acts. This is how subroutines and functions
communicate with the rest of the application.
Subroutines and functions may accept any number of arguments, and you must supply a value
for each argument of the procedure when you call it. Some of the arguments may be optional,
which means you can omit them; you will see shortly how to handle optional arguments.
The custom function Min(), for instance, accepts two numbers and returns the smaller one:
Function Min(ByVal a As Single, ByVal b As Single) As Single
Min = IIf(a < b, a, b)
End Function
IIf() is a built-in function that evaluates the first argument, which is a logical expression. If
the expression is True, the IIf() function returns the second argument. If the expression is False,
the function returns the third argument.
To call the Min() custom function, use a few statements like the following:
Dim val1 As Single = 33.001
Dim val2 As Single = 33.0011
Dim smallerVal as Single
smallerVal = Min(val1, val2)
Debug.Write(”The smaller value is ” & smallerVal)
If you execute these statements (place them in a button’s Click event handler), you will see the
following in the Immediate window:
The smaller value is 33.001
If you attempt to call the same function with two Double values, with a statement like the
following, you will see the value 3.33 in the Immediate window:
Debug.WriteLine(Min(3.33000000111, 3.33000000222))Petroutsos c03.tex V2 - 01/28/2008 1:01pm Page 104
104 CHAPTER 3 PROGRAMMING FUNDAMENTALS
The compiler converted the two values from Double to Single data type and returned one of
them. Which one is it? It doesn’t make a difference because when converted to Single, both values
are the same.
Interesting things will happen if you attempt to use the Min() function with the Strict option
turned on. Insert the statement Option Strict On at the very beginning of the file, or set Option
Strict to On in the Compile tab of the project’s Properties pages. The editor will underline the
statement that implements the Min() function: the IIf() function. The IIf() function accepts
two Object variables as arguments, and returns one of them as its result. The Strict option prevents
the compiler from converting an Object to a numeric variable. To use the IIf() function with the
Strict option, you must change its implementation as follows:
Function Min(ByVal a As Object, ByVal b As Object) As Object
Min = IIf(Val(a) < Val(b), a, b)
End Function
It’s possible to implement a Min() function that can compare arguments of all types (integers,
strings, dates, and so on). We’ll return to this sample function later in this chapter, in the section
‘‘Overloading Functions.’’
Argument-Passing Mechanisms
One of the most important topics in implementing your own procedures is the mechanism used
to pass arguments. The examples so far have used the default mechanism: passing arguments by
value. The other mechanism is passing them by reference. Although most programmers use the
default mechanism, it’s important to know the difference between the two mechanisms and when
to use each.
By Value versus by Reference
When you pass an argument by value, the procedure sees only a copy of the argument. Even if
the procedure changes it, the changes aren’t permanent; in other words, the value of the original
variable passed to the procedure isn’t affected. The benefit of passing arguments by value is that
the argument values are isolated from the procedure, and only the code segment in which they
are declared can change their values. This is the default argument-passing mechanism in Visual
Basic 2008.
In VB 6, the default argument-passing mechanism was by reference, and this is something you
should be aware of, especially if you’re migrating VB 6 code to VB 2008.
To specify the arguments that will be passed by value, use the ByVal keyword in front of the
argument’s name. If you omit the ByVal keyword, the editor will insert it automatically because
it’s the default option. To declare that the Degrees() function’s argument is passed by value, use
the ByVal keyword in the argument’s declaration as follows:
Function Degrees(ByVal Celsius as Single) As Single
Return((9 / 5) * Celsius + 32)
End FunctionPetroutsos c03.tex V2 - 01/28/2008 1:01pm Page 105
ARGUMENTS 105
To see what the ByVal keyword does, add a line that changes the value of the argument
in the function:
Function Degrees(ByVal Celsius as Single) As Single
Return((9 / 5) * Celsius + 32)
Celsius = 0
End Function
Now call the function as follows:
CTemp = InputBox(”Enter temperature in degrees Celsius”)
MsgBox(CTemp.ToString & ” degrees Celsius are ” &
Degrees((CTemp)) & ” degrees Fahrenheit”)
If you enter the value 32, the following message is displayed:
32 degrees Celsius are 89.6 degrees Fahrenheit
Replace the ByVal keyword with the ByRef keyword in the function’s definition and call the
function as follows:
Celsius = 32.0
FTemp = Degrees(Celsius)
MsgBox(Celsius.ToString & ” degrees Celsius are ” & FTemp &
” degrees Fahrenheit”)
This time the program displays the following message:
0 degrees Celsius are 89.6 degrees Fahrenheit
When the Celsius argument was passed to the Degrees() function, its value was 32. But
the function changed its value, and upon return it was 0. Because the argument was passed by
reference, any changes made by the procedure affected the variable permanently. As a result,
when the calling program attempted to use it, the variable had a different value than expected.
Returning Multiple Values
If you want to write a function that returns more than a single result, you will most likely pass
additional arguments by reference and set their values from within the function’s code. The
CalculateStatistics() function, shown a little later in this section, calculates the basic statistics
of a data set. The values of the data set are stored in an array, which is passed to the function
by reference. The CalculateStatistics() function must return two values: the average and
standard deviation of the data set. Here’s the declaration of the CalculateStatistics() function:
Function CalculateStatistics(ByRef Data() As Double,
ByRef Avg As Double, ByRef StDev As Double) As IntegerPetroutsos c03.tex V2 - 01/28/2008 1:01pm Page 106
106 CHAPTER 3 PROGRAMMING FUNDAMENTALS
The function returns an integer, which is the number of values in the data set. The two impor-
tant values calculated by the function are returned in the Avg and StDev arguments:
Function CalculateStatistics(ByRef Data() As Double,
ByRef Avg As Double, ByRef StDev As Double) As Integer
Dim i As Integer, sum As Double, sumSqr As Double, points As Integer
points = Data.Length
Fori=0To points - 1
sum = sum + Data(i)
sumSqr = sumSqr + Data(i) ˆ 2
Next
Avg = sum / points
StDev = System.Math.Sqrt(sumSqr / points - Avg ˆ 2)
Return(points)
End Function
To call the CalculateStatistics() function fromwithin your code, set up an array of Doubles
and declare two variables that will hold the average and standard deviation of the data set:
Dim Values(99) As Double
‘ Statements to populate the data set
Dim average, deviation As Double
Dim points As Integer
points = Stats(Values, average, deviation)
Debug.WriteLine points & ” values processed.”
Debug.WriteLine ”The average is ” & average & ” and”
Debug.WriteLine ”the standard deviation is ” & deviation
Using ByRef arguments is the simplest method for a function to return multiple values. How-
ever, the definition of your functionsmight become cluttered, especially if you want to returnmore
than a few values. Another problem with this technique is that it’s not clear whether an argument
must be set before calling the function. As you will see shortly, it is possible for a function to return
an array or a custom structure with fields for any number of values.
Passing Objects as Arguments
When you pass objects as arguments, they’re passed by reference, even if you have specified the
ByVal keyword. The procedure can access and modify the members of the object passed as an
argument, and the new value will be visible in the procedure that made the call.
The following code segment demonstrates this. The object is an ArrayList, which is an enhanced
form of an array. The ArrayList is discussed in detail later in the book, but to follow this example
all you need to know is that the Add method adds new items to the ArrayList, and you can access
individual items with an index value, similar to an array’s elements. In the Click event handler
of a Button control, create a new instance of the ArrayList object and call the PopulateList()
subroutine to populate the list. Even if the ArrayList object is passed to the subroutine by value,
the subroutine has access to its items:
Private Sub Button1 Click(ByVal sender As System.Object,
ByVal e As System.EventArgs) Handles Button1.Click
Dim aList As New ArrayList()
PopulateList(aList)Petroutsos c03.tex V2 - 01/28/2008 1:01pm Page 107
ARGUMENTS 107
Debug.WriteLine(aList(0).ToString)
Debug.WriteLine(aList(1).ToString)
Debug.WriteLine(aList(2).ToString)
End Sub
Sub PopulateList(ByVal list As ArrayList)
list.Add(”1”)
list.Add(”2”)
list.Add(”3”)
End Sub
The same is true for arrays and all other collections. Even if you specify the ByVal keyword,
they’re passed by reference. A more elegant method of modifying the members of a structure from
within a procedure is to implement the procedure as a function returning a structure, as explained
in the section ‘‘Functions Returning Structures,’’ later in this chapter.
Built-in Functions
VB 2008 provides many functions that implement common or complicated tasks, and you can look
them up in the documentation. (You’ll find them in the Visual Studio Visual Basic  Reference
 Functions branch of the contents tree in the Visual Studio documentation.) There are functions
for the common math operations, functions to perform calculations with dates (these are truly
complicated operations), financial functions, andmany more.When you use the built-in functions,
you don’t have to know how they work internally — just how to call them and how to retrieve the
return value.
The Pmt() function, for example, calculates the monthly payments on a loan. All you have to
know is the arguments you must pass to the function and how to retrieve the result. The syntax
of the Pmt() function is the following, where MPay is the monthly payment, Rate is the monthly
interest rate, and NPer is the number of payments (the duration of the loan in months). PV is the
loan’s present value (the amount you took from the bank):
MPay = Pmt(Rate, NPer, PV, FV, Due)
Due is an optional argument that specifies when the payments are due (the beginning or the end
of the month), and FV is another optional argument that specifies the future value of an amount.
This isn’t needed in the case of a loan, but it can help you calculate how much money you should
deposit each month to accumulate a target amount over a given time. (The amount returned by
the Pmt() function is negative because it’s a negative cash flow— it’s money you owe — so pay
attention to the sign of your values.)
To calculate the monthly payment for a $20,000 loan paid off over a period of six years at a
fixed interest rate of 7.25%, you call the Pmt() function, as shown in Listing 3.8.
Listing 3.8: Using the Pmt() Built-in Function
Dim mPay, totalPay As Double
Dim Duration As Integer = 6 * 12
Dim Rate As Single = (7.25 / 100) / 12
Dim Amount As Single = 20000
mPay = -Pmt(Rate, Duration, Amount)Petroutsos c03.tex V2 - 01/28/2008 1:01pm Page 108
108 CHAPTER 3 PROGRAMMING FUNDAMENTALS
totalPay = mPay * Duration
MsgBox(”Your monthly payment will be ” & mPay.ToString(”C”) &
vbCrLf & ”You will pay back a total of ” &
totalPay.ToString(”C”))
Notice that the interest (7.25%) is divided by 12 because the function requires the monthly
interest. The value returned by the function is the monthly payment for the loan specified with
the Duration, Amount,and Rate variables. If you place the preceding lines in the Click event
handler of a Button, run the project, and then click the button, the following message will appear
in a message box:
Your monthly payment will be $343.39
You will pay back a total of $24,723.80
Let’s say you want to accumulate $40,000 over the next 15 years by making monthly deposits
of equal amounts. To calculate the monthly deposit amount, you must call the Pmt() function,
passing 0 as the present value and the target amount as the future value. Replace the statements
in the button’s Click event handler with the following and run the project:
Dim mPay As Double
Dim Duration As Integer = 15 * 12
Dim Rate As Single = (4.0 / 100.0) / 12
Dim Amount As Single = -40000.0
mPay = Pmt(Rate, Duration, 0, Amount)
MsgBox(”A monthly deposit of ” & mPay.ToString(”C”) & vbCrLf &
”every month will yield $40,000 in 15 years”)
It turns out that if you want to accumulate $40,000 over the next 15 years to send your kid to
college, assuming a constant interest rate of 4%, you must deposit $162.54 every month. You’ll put
out almost $30,000, and the rest will be the interest you earn.
Pmt() is one of the simpler financial functions provided by the Framework, but most of us
would find it really difficult to write the code for this function. Because financial calculations are
quite common in business programming, many of the functions you might need already exist, and
all you need to know is how to call them. If you’re developing financial applications, you should
look up the financial functions in the documentation.
Let’s look at another useful built-in function, the MonthName() function, which accepts as an
argument amonth number and returns the name of themonth. This function is not as trivial as you
might think because it returns the month name or its abbreviation in the language of the current
culture. The MonthName() function accepts as arguments the month number and a True/False
value that determines whether it will return the abbreviation or the full name of the month. The
following statements display the name of the current month (both the abbreviation and the full
name). Every time you execute these statements, you will see the current month’s name in the
current language:
Dim mName As String
mName = MonthName(Now.Month, True)
MsgBox(mName) ‘ prints ”Jan”
mName = MonthName(Now.Month, False)
MsgBox(mName) ‘ prints ”January”Petroutsos c03.tex V2 - 01/28/2008 1:01pm Page 109
ARGUMENTS 109
A similar function, the WeekDayName() function, returns the name of the week for a specific
weekday. This function accepts an additional argument that determines the first day of the week.
(See the documentation for more information on the syntax of the WeekDayName() function.)
The primary role of functions is to extend the functionality of the language.Many functions that
perform rather common practical operations have been included in the language, but they aren’t
nearly enough for the needs of all developers or all types of applications. Besides the built-in func-
tions, you can write custom functions to simplify the development of your custom applications, as
explained in the following section.
Custom Functions
Most of the code we write is in the form of custom functions or subroutines that are called from
several places in the application. Subroutines are just like functions, except that they don’t return a
value, so we’ll focus on the implementation of custom functions.With the exception of a function’s
return value, everything else presented in this and the following section applies to subroutines
as well.
Let’s look at an example of a fairly simple (but not trivial) function that does something really
useful. Books are identified by a unique international standard book number (ISBN), and every
application that manages books needs a function to verify the ISBN, which is made up of 12 dig-
its followed by a check digit. To calculate the check digit, you multiply each of the 12 digits by a
constant; the first digit is multiplied by 1, the second digit is multiplied by 3, the third digit by 1
again, and so on. The sum of these multiplications is then divided by 10, and we take the remain-
der. The check digit is this remainder subtracted from 10. To calculate the check digit for the ISBN
978078212283, compute the sum of the following products:
9*1+7*3+8*1+0*3+7*1+8*3+
2*1+1*3+2*1+2*3+8*1+3*3=99
The sum is 99; when you divide it by 10, the remainder is 9. The check digit is 10 – 9, or 1, and
the book’s complete ISBN is 9780782122831. The ISBNCheckDigit() function, shown in Listing
3.9, accepts the 12 digits of the ISBN as an argument and returns the appropriate check digit.
Listing 3.9: The ISBNCheckDigit() CustomFunction
Function ISBNCheckDigit(ByVal ISBN As String) As String
Dim i As Integer, chksum As Integer = 0
Dim chkDigit As Integer
Dim factor As Integer = 3
Fori=0To11
factor = 4 - factor
chksum += factor * Convert.ToInt16(ISBN.Substring(i, 1))
Next
Return (((10 - (chksum Mod 10)) Mod 10)).ToString
End Function
The ISBNCheckDigit() function returns a string value because ISBNs are handled as strings,
not numbers. (Leading zeros are important in an ISBN but are totally meaningless, and omitted,
in a numeric value.) The Substring method of a String object extracts a number of characters
from the string to which it’s applied. The first argument is the starting location in the string, andPetroutsos c03.tex V2 - 01/28/2008 1:01pm Page 110
110 CHAPTER 3 PROGRAMMING FUNDAMENTALS
the second is the number of characters to be extracted. The expression ISBN.Substring(i, 1)
extracts one character at a time from the ISBN string variable. During the first iteration of the loop,
it extracts the first character; during the second iteration, it extracts the second character, and
so on.
The extracted character is a numeric digit, which is multiplied by the factor variable value
and the result is added to the chkSum variable. This variable is the checksum of the ISBN. After it
has been calculated, we divide it by 10 and take its remainder (the first Mod operator returns the
remainder of this division), which we subtract from 10. The second Mod operator maps the value
10 to 0. This is the ISBN’s check digit and the function’s return value.
You can use this function in an application that maintains a book database to make sure that
all books are entered with a valid ISBN. You can also use it with a web application that allows
viewers to request books by their ISBN. The same code will work with two different applications,
even when passed to other developers. Developers using your function don’t have to know how
the check digit is calculated, just how to call the function and retrieve its result.
To test the ISBNCheckDigit() function, start a new project, place a button on the form, and
enter the following statements in its Click event handler (or open the ISBN project in the folder
with this chapter’s sample projects):
Private Sub Button1 Click(ByVal sender As System.Object,
ByVal e As System.EventArgs) Handles Button1.Click
Console.WriteLine(”The check Digit is ” &
ISBNCheckDigit(”978078212283”))
End Sub
After inserting the code of the ISBNCheckDigit() function and the code that calls the func-
tion, your code editor should look like Figure 3.1. You can place a TextBox control on the form
and pass the Text property of the control to the ISBNCheckDigit() function to calculate the
check digit.
A similar algorithm is used for calculating the check digit of credit cards: the Luhns algo-
rithm. You can look it up on the Internet and write a custom function for validating credit
card numbers.
Figure 3.1
Calling the ISBNCheck-
Digit() functionPetroutsos c03.tex V2 - 01/28/2008 1:01pm Page 111
ARGUMENTS 111
Passing Arguments and Returning Values
So far you’ve learned how to write and call procedures with a few simple arguments and how to
retrieve the function’s return value and use it in your code. This section covers a few advanced
topics on argument-passing techniques and how to write functions that return multiple values, or
arrays of values and custom data types.
Passing an Unknown Number of Arguments
Generally, all the arguments that a procedure expects are listed in the procedure’s definition,
and the program that calls the procedure must supply values for all arguments. On occasion,
however, you might not know how many arguments will be passed to the procedure. Procedures
that calculate averages or, in general, process multiple values can accept from a few to several
arguments whose count is not known at design time. VB 2008 supports the ParamArray keyword,
which allows you to pass a variable number of arguments to a procedure.
Let’s look at an example. Suppose that you want to populate a ListBox control with elements.
To add an item to the ListBox control, you call the Add method of its Items collection as follows:
ListBox1.Items.Add(”new item”)
This statement adds the string new item to the ListBox1 control. If you frequently add multiple
items to a ListBox control from within your code, you can write a subroutine that performs this
task. The following subroutine adds a variable number of arguments to the ListBox1 control:
Sub AddNamesToList(ByVal ParamArray NamesArray() As Object)
Dim x As Object
For Each x In NamesArray
ListBox1.Items.Add(x)
Next x
End Sub
This subroutine’s argument is an array prefixed with the keyword ParamArray, which holds
all the parameters passed to the subroutine. If the parameter array holds items of the same type,
you can declare the array to be of the specific type (string, integer, and so on). To add items to the
list, call the AddNamesToList() subroutine as follows:
AddNamesToList(”Robert”, ”Manny”, ”Renee”, ”Charles”, ”Madonna”)
If you want to know the number of arguments actually passed to the procedure, use the Length
property of the parameter array. The number of arguments passed to the AddNamesToList()
subroutine is given by the following expression:
NamesArray.Length
The following loop goes through all the elements of the NamesArray and adds them to the list:
Dim i As Integer
Fori=0to NamesArray.GetUpperBound(0)
ListBox1.Items.Add(NamesArray(i))
Next iPetroutsos c03.tex V2 - 01/28/2008 1:01pm Page 112
112 CHAPTER 3 PROGRAMMING FUNDAMENTALS
VB arrays are zero-based (the index of the first item is 0), and the GetUpperBound method
returns the index of the last item in the array.
A procedure that accepts multiple arguments relies on the order of the arguments. To omit
some of the arguments, you must use the corresponding comma. Let’s say you want to call such
a procedure and specify the first, third, and fourth arguments. The procedure must be called as
follows:
ProcName(arg1, , arg3, arg4)
The arguments to similar procedures are usually of equal stature, and their order doesn’t make
any difference. A function that calculates the mean or other basic statistics of a set of numbers,
or a subroutine that populates a ListBox or ComboBox control, are prime candidates for imple-
menting this technique. If the procedure accepts a variable number of arguments that aren’t equal
in stature, you should consider the technique described in the following section. If the function
accepts a parameter array, this must the last argument in the list, and none of the other parameters
can be optional.
Named Arguments
You learned how to write procedures with optional arguments and how to pass a variable number
of arguments to the procedure. The main limitation of the argument-passing mechanism, though,
is the order of the arguments. By default, Visual Basic matches the values passed to a procedure to
the declared arguments by their order (which is why the arguments you’ve seen so far are called
positional arguments).
This limitation is lifted by Visual Basic’s capability to specify named arguments.Withnamed
arguments, you can supply arguments in any order because they are recognized by name and not
by their order in the list of the procedure’s arguments. Suppose you’ve written a function that
expects three arguments: a name, an address, and an email address:
Function Contact(Name As String, Address As String, EMail As String)
When calling this function, you must supply three strings that correspond to the arguments
Name, Address,and EMail, in that order. However, there’s a safer way to call this function: Supply
the arguments in any order by their names. Instead of calling the Contact() function as follows:
Contact(”Peter Evans”, ”2020 Palm Ave., Santa Barbara, CA 90000”,
”PeterEvans@example.com”)
you can call it this way:
Contact(Address:=”2020 Palm Ave., Santa Barbara, CA 90000”,
EMail:=”PeterEvans@example.com”, Name:=”Peter Evans”)
The := operator assigns values to the named arguments. Because the arguments are passed by
name, you can supply them in any order.
To test this technique, enter the following function declaration in a form’s code:
Function Contact(ByVal Name As String, ByVal Address As String,
ByVal EMail As String) As String
Debug.WriteLine(Name)Petroutsos c03.tex V2 - 01/28/2008 1:01pm Page 113
ARGUMENTS 113
Debug.WriteLine(Address)
Debug.WriteLine(EMail)
Return (”OK”)
End Function
Then call the Contact() function from within a button’s Click event with the following
statement:
Debug.WriteLine(
Contact(Address:=”2020 Palm Ave., Santa Barbara, CA 90000”,
Name:=”Peter Evans”, EMail:=”PeterEvans@example.com”))
You’ll see the following in the Immediate window:
Peter Evans
2020 Palm Ave., Santa Barbara, CA 90000
PeterEvans@example.com
OK
The function knows which value corresponds to which argument and can process them the
same way that it processes positional arguments. Notice that the function’s definition is the same,
whether you call it with positional or named arguments. The difference is in how you call the
function and not how you declare it.
Named arguments make code safer and easier to read, but because they require a lot of typing,
most programmers don’t use them. Besides, when IntelliSense is on, you can see the definition of
the function as you enter the arguments, and this minimizes the chances of swapping two values
by mistake.
More Types of Function Return Values
Functions are not limited to returning simple data types such as integers or strings. They might
return custom data types and even arrays. The capability of functions to return all types of data
makes them very flexible and can simplify coding, so we’ll explore it in detail in the following
sections. Using complex data types, such as structures and arrays, allows you to write functions
that return multiple values.
Functions Returning Structures
Suppose you need a function that returns a customer’s savings and checking account balances.
So far, you’ve learned that you can return two or more values from a function by supplying
arguments with the ByRef keyword. A more elegant method is to create a custom data type
(a structure) and write a function that returns a variable of this type.
Here’s a simple example of a function that returns a custom data type. This example outlines
the steps you must repeat every time you want to create functions that return custom data types:
1. Create a new project and insert the declarations of a custom data type in the declarations
section of the form:
Structure CustBalance
Dim SavingsBalance As Decimal
Dim CheckingBalance As Decimal
End StructurePetroutsos c03.tex V2 - 01/28/2008 1:01pm Page 114
114 CHAPTER 3 PROGRAMMING FUNDAMENTALS
2. Implement the function that returns a value of the custom type. In the function’s body, you
must declare a variable of the type returned by the function and assign the proper values
to its fields. The following function assigns random values to the fields CheckingBalance
and SavingsBalance. Then assign the variable to the function’s name, as shown next:
Function GetCustBalance(ID As Long) As CustBalance
Dim tBalance As CustBalance
tBalance.CheckingBalance = CDec(1000 + 4000 * rnd())
tBalance.SavingsBalance = CDec(1000 + 15000 * rnd())
Return(tBalance)
End Function
3. Place a button on the form from which you want to call the function. Declare a variable
of the same type and assign to it the function’s return value. The example that follows prints
the savings and checking balances in the Output window:
Private Sub Button1 Click(...) Handles Button1.Click
Dim balance As CustBalance
balance = GetCustBalance(1)
Debug.WriteLine(balance.CheckingBalance)
Debug.WriteLine(balance.SavingsBalance)
End Sub
The code shown in this section belongs to the Structures sample project. Create this project from
scratch, perhaps by using your own custom data type, to explore its structure and experiment with
functions that return custom data types. In Chapter 10, ‘‘Building Custom Classes,’’ you’ll learn
how to build your own classes and you’ll see how to write functions that return custom objects.
VB 2008 at Work: The Types Project
The Types project, which you’ll find in this chapter’s folder, demonstrates a function that returns a
custom data type. The Types project consists of a form that displays record fields (see Figure 3.2).
Figure 3.2
The Types project
demonstrates functions
that return custom data
types.Petroutsos c03.tex V2 - 01/28/2008 1:01pm Page 115
ARGUMENTS 115
Every time you click the Next button, the fields of the next record are displayed in the correspond-
ing TextBox controls on the form. When all records are exhausted, the program wraps back to the
first record.
The project consists of a single form and uses a custom data type, implemented with the follow-
ing structure. The structure’s declaration must appear in the form’s code, outside any procedure,
along with a couple of variable declarations:
Structure Customer
Dim Company As String
Dim Manager As String
Dim Address As String
Dim City As String
Dim Country As String
Dim CustomerSince As Date
Dim Balance As Decimal
End Structure
Private Customers(9) As Customer
Private cust As Customer
Private currentIndex as Integer
The array Customers holds the data for 10 customers, and the cust variable is used as a tem-
porary variable for storing the current customer’s data. The currentIndex variable is the index
of the current element of the array. The array is filled with Customer data, and the currentIndex
variable is initialized to zero.
The Click event handler of the Next button calls the GetCustomer() function with an index
value (which is the order of the current customer) to retrieve the data of the next customer, and
displays the customer’s fields on the Label controls on the form with the ShowCustomer() subrou-
tine. Then it increases the value of the currentIndex variable to point to the current customer’s
index. You can open the Types project in Visual Studio and examine its code, which contains quite
a few comments explaining its operation.
Functions Returning Arrays
In addition to returning custom data types, VB 2008 functions can also return arrays. This is an
interesting possibility that allows you to write functions that return not only multiple values, but
also an unknown number of values.
In this section, we’ll write the Statistics() function, similar to the CalculateStatistics()
function you saw a little earlier in this chapter. The Statistics() function returns the statistics in
an array. Moreover, it returns not only the average and the standard deviation, but the minimum
and maximum values in the data set as well. One way to declare a function that calculates all the
statistics is as follows:
Function Statistics(ByRef DataArray() As Double) As Double()
This function accepts an array with the data values and returns an array of Doubles. To imple-
ment a function that returns an array, you must do the following:
1. Specify a type for the function’s return value and add a pair of parentheses after the type’s
name. Don’t specify the dimensions of the array to be returned here; the array will be
declared formally in the function.Petroutsos c03.tex V2 - 01/28/2008 1:01pm Page 116
116 CHAPTER 3 PROGRAMMING FUNDAMENTALS
2. In the function’s code, declare an array of the same type and specify its dimensions. If the
function should return four values, use a declaration like this one:
Dim Results(3) As Double
The Results array, which will be used to store the results, must be of the same type as the
function— its name can be anything.
3. To return the Results array, simply use it as an argument to the Return statement:
Return(Results)
4. In the calling procedure, you must declare an array of the same type without dimensions:
Dim Statistics() As Double
5. Finally, you must call the function and assign its return value to this array:
Stats() = Statistics(DataSet())
Here, DataSet is an array with the values whose basic statistics will be calculated by the
Statistics() function. Your code can then retrieve each element of the array with an index
value as usual.
VB 2008 at Work: The Statistics Project
The Statistics sample project demonstrates how to write a procedure that returns an array. When
you run it, the Statistics application creates a data set of random values and then calls the Statis-
tics() function to calculate the data set’s basic statistics. The results are returned in an array,
and the main program displays them in Label controls, as shown in Figure 3.3. Every time the
Calculate Statistics button is clicked, a new data set is generated and its statistics are displayed.
Figure 3.3
The Statistics project
calculates the basic
statistics of a data set
and returns them in an
array.Petroutsos c03.tex V2 - 01/28/2008 1:01pm Page 117
ARGUMENTS 117
The Statistics() function’s code is based on the preceding discussion, and I will not show it
here. You can open the Statistics project and examine the function’s code, as well as how the main
program uses the array returned by the Statistics() function.
Overloading Functions
There are situations in which the same function must operate on different data types or a differ-
ent number of arguments. In the past, you had to write different functions, with different names
and different arguments, to accommodate similar requirements. The Framework introduced the
concept of function overloading, which means that you can have multiple implementations of
the same function, each with a different set of arguments and possibly a different return value.
Yet all overloaded functions share the same name. Let me introduce this concept by examining
one of the many overloaded functions that come with the .NET Framework.
The Next method of the System.Random class returns an integer value from –2,147,483,648 to
2,147,483,647. (This is the range of values that can be represented by the Integer data type.) We
should also be able to generate random numbers in a limited range of integer values. To emulate
the throw of a die, we want a random value in the range from 1 to 6, whereas for a roulette game
we want an integer random value in the range from 0 to 36. You can specify an upper limit for the
random number with an optional integer argument. The following statement will return a random
integer in the range from 0 to 99:
randomInt = rnd.Next(100)
You can also specify both the lower and upper limits of the random number’s range. The
following statement will return a random integer in the range from 1,000 to 1,999:
randomInt = rnd.Next(1000, 2000)
The same method behaves differently based on the arguments we supply. The behavior of the
method depends either on the type of the arguments, the number of the arguments, or both. As
you will see, there’s no single function that alters its behavior based on its arguments. There are as
many different implementations of the same function as there are argument combinations. All
the functions share the same name, so they appear to the user as a single multifaceted function.
Thesefunctionsareoverloaded,andyou’llseehow they’re implemented in the following section.
If you haven’t turned off the IntelliSense feature of the editor, as soon as you type the opening
parenthesis after a function or method name, you see a yellow box with the syntax of the function
or method. You’ll know that a function, or a method, is overloaded when this box contains a
number and two arrows. Each number corresponds to a different overloaded form, and you can
move to the next or previous overloaded form by clicking the two little arrows or by pressing the
arrow keys.
Let’s return to the Min() function we implemented earlier in this chapter. The initial imple-
mentation of the Min() function is shown next:
Function Min(ByVal a As Double, ByVal b As Double) As Double
Min = IIf(a < b, a, b)
End Function
By accepting Double values as arguments, this function can handle all numeric types. VB 2008
performs automatic widening conversions (it can convert Integers and Decimals to Doubles),Petroutsos c03.tex V2 - 01/28/2008 1:01pm Page 118
118 CHAPTER 3 PROGRAMMING FUNDAMENTALS
so this trick makes the function work with all numeric data types. However, what about strings?
If you attempt to call the Min() function with two strings as arguments, you’ll get an exception.
The Min() function just can’t handle strings.
To write a Min() function that can handle both numeric and string values, youmust, in essence,
write two Min() functions. All Min() functions must be prefixed with the Overloads keyword.
The following statements show two different implementations of the same function:
Overloads Function Min(ByVal a As Double, ByVal b As Double) As Double
Min = Convert.ToDouble(IIf(a < b, a, b))
End Function
Overloads Function Min(ByVal a As String, ByVal b As String) As String
Min = Convert.ToString(IIf(a < b, a, b))
End Function
We need a third overloaded form of the same function to compare dates. If you call the Min()
function, passing as an argument two dates, as in the following statement, the Min() function will
compare them as strings and return (incorrectly) the first date.
Debug.WriteLine(Min(#1/1/2009#, #3/4/2008#))
This statement is not even valid when the Strict option is on, so you clearly need another over-
loaded form of the function that accepts two dates as arguments, as shown here:
Overloads Function Min(ByVal a As Date, ByVal b As Date) As Date
Min = IIf(a < b, a, b)
End Function
If you now call the Min() function with the dates #1/1/2009# and #3/4/2008#, the function
will return the second date, which is chronologically smaller than the first.
Let’s look into a more complicated overloaded function, which makes use of some topics dis-
cussed later in this book. The CountFiles() function counts the number of files in a folder that
meet certain criteria. The criteria could be the size of the files, their type, or the date they were
created. You can come up with any combination of these criteria, but the following are the most
useful combinations. (These are the functions I would use, but you can create even more combina-
tions or introduce new criteria of your own.) The names of the arguments are self-descriptive, so I
need not explain what each form of the CountFiles() function does.
CountFiles(ByVal minSize As Integer, ByVal maxSize As Integer) As Integer
CountFiles(ByVal fromDate As Date, ByVal toDate As Date) As Integer
CountFiles(ByVal type As String) As Integer
CountFiles(ByVal minSize As Integer, ByVal maxSize As Integer,
ByVal type As String) As Integer
CountFiles(ByVal fromDate As Date, ByVal toDate As Date,
ByVal type As String) As Integer
Listing 3.10 shows the implementation of these overloaded forms of the CountFiles() func-
tion. (I’mnot showing all overloaded forms of the function; you can open the OverloadedFunctions
project in the IDE and examine the code.) Because we haven’t discussed file operations yet, most
of the code in the function’s body will be new to you — but it’s not hard to follow. For the benefitPetroutsos c03.tex V2 - 01/28/2008 1:01pm Page 119
ARGUMENTS 119
of readers who are totally unfamiliar with file operations, I included a statement that prints in the
Immediate window the type of files counted by each function. The Debug.WriteLine statement
prints the values of the arguments passed to the function, along with a description of the type of
search it will perform. The overloaded form that accepts two integer values as arguments prints
something like this:
You’ve requested the files between 1000 and 100000 bytes
whereas the overloaded form that accepts a string as an argument prints the following:
You’ve requested the .EXE files
Listing 3.10: The Overloaded Implementations of the CountFiles() Function
Overloads Function CountFiles(
ByVal minSize As Integer,
ByVal maxSize As Integer) As Integer
Debug.WriteLine(”You’ve requested the files between ” &
minSize & ” and ” & maxSize & ” bytes”)
Dim files() As String
files = System.IO.Directory.GetFiles(”c:\windows”)
Dim i, fileCount As Integer
Fori=0To files.GetUpperBound(0)
Dim FI As New System.IO.FileInfo(files(i))
If FI.Length >= minSize And FI.Length <= maxSize Then
fileCount = fileCount + 1
End If
Next
Return(fileCount)
End Function
Overloads Function CountFiles(
ByVal fromDate As Date,
ByVal toDate As Date) As Integer
Debug.WriteLine(”You’ve requested the count of files created from ” &
fromDate & ” to ” & toDate)
Dim files() As String
files = System.IO.Directory.GetFiles(”c:\windows”)
Dim i, fileCount As Integer
Fori=0To files.GetUpperBound(0)
Dim FI As New System.IO.FileInfo(files(i))
If FI.CreationTime.Date >= fromDate And
FI.CreationTime.Date <= toDate Then
fileCount = fileCount + 1
End If
Next
Return(fileCount)
End FunctionPetroutsos c03.tex V2 - 01/28/2008 1:01pm Page 120
120 CHAPTER 3 PROGRAMMING FUNDAMENTALS
Overloads Function CountFiles(ByVal type As String) As Integer
Debug.WriteLine(”You’ve requested the ” & type & ” files”)
‘ Function Implementation
End Function
Overloads Function CountFiles(
ByVal minSize As Integer,
ByVal maxSize As Integer,
ByVal type As String) As Integer
Debug.WriteLine(”You’ve requested the ” & type &
” files between ” & minSize & ” and ” &
maxSize & ” bytes”)
‘ Function implementation
End Function
Overloads Function CountFiles(ByVal fromDate As Date,
ByVal toDate As Date, ByVal type As String) As Integer
Debug.WriteLine(”You’ve requested the ” & type &
” files created from ” & fromDate & ” to ” & toDate)
‘ Function implementation
End Function
If you’re unfamiliar with the Directory and File objects, focus on the statement that prints to the
Immediate window and ignore the statements that actually count the files that meet the specified
criteria. After reading Chapter 15, ‘‘Accessing Folders and Files,’’ you can revisit this example and
understand the statements that select the qualifying files and count them.
Start a new project and enter the definitions of the overloaded forms of the function on the
form’s level. Listing 3.10 is lengthy, but all the overloaded functions have the same structure and
differ only in how they select the files to count. Then place a TextBox and a button on the form, as
shown in Figure 3.4, and enter a few statements that exercise the various overloaded forms of the
function (such as the ones shown in Listing 3.11) in the button’s Click event handler.
Listing 3.11: Testing the Overloaded Forms of the CountFiles() Function
Private Sub Button1 Click(...) Handles Button1.Click
TextBox1.AppendText(CountFiles(1000, 100000) &
” files with size between 1KB and 100KB” & vbCrLf)
TextBox1.AppendText(CountFiles(#1/1/2006#, #12/31/2006#) &
” files created in 2006” & vbCrLf)
TextBox1.AppendText(CountFiles(”.BMP”) & ” BMP files” & vbCrLf)
TextBox1.AppendText(CountFiles(1000, 100000, ”.EXE”) &
” EXE files between 1 and 100 KB” & vbCrLf)
TextBox1.AppendText(CountFiles(#1/1/2006#, #12/31/2007#, ”.EXE”) &
” EXE files created in 2006 and 2007”)
End SubPetroutsos c03.tex V2 - 01/28/2008 1:01pm Page 121
THE BOTTOM LINE 121
Figure 3.4
The Overloaded
Functions project
The button calls the various overloaded forms of the CountFiles() function one after the other
and prints the results on the TextBox control. From now on, I’ll be omitting the list of arguments
in the most common event handlers, such as the Click event handler, because they’re always the
same and they don’t add to the readability of the code. In place of the two arguments, I’ll insert an
ellipsis to indicate the lack of the arguments.
Function overloading is used heavily throughout the language. There are relatively few func-
tions (or methods, for that matter) that aren’t overloaded. Every time you enter the name of a
function followed by an opening parenthesis, a list of its arguments appears in the drop-down list
with the arguments of the function. If the function is overloaded, you’ll see a number in front of
the list of arguments, as shown in Figure 3.5. This number is the order of the overloaded form
of the function, and it’s followed by the arguments of the specific form of the function. The figure
shows all the forms of the CountFiles() function.
Figure 3.5
The overloaded forms
of the CountFiles()
function
The BottomLine
Use Visual Basic’s flow-control statements. Visual Basic provides several statements for
controlling the sequence in which statements are executed: decision statements, which change
the course of execution based on the outcome of a comparison, and loop statements, which
repeat a number of statements while a condition is true or false.
Master It Explain briefly the decision statements of Visual Basic.Petroutsos c03.tex V2 - 01/28/2008 1:01pm Page 122
122 CHAPTER 3 PROGRAMMING FUNDAMENTALS
Write subroutines and functions. To manage large applications, we break our code into
small, manageable units. These units of code are the subroutines and functions. Subroutines
perform actions and don’t return any values. Functions, on the other hand, perform calcula-
tions and return values. Most of the language’s built-in functionality is in the form of functions.
Master It How will you create multiple overloaded forms of the same function?
Pass arguments to subroutines and functions. Procedures and functions communicate with
one another via arguments, which are listed in a pair of parentheses following the procedure’s
name. Each argument has a name and a type. When you call the procedure, you must supply
values for each argument and the types of the values should match the types listed in the pro-
cedure’s definition.
Master It Explain the difference between passing arguments by value and passing
arguments by reference.Petroutsos c04.tex V3 - 01/28/2008 12:36pm Page 123
Chapter 4
GUI Design and Event-Driven
Programming
The first three chapters of this book introduced you to the basics of designing applications with
Visual Studio 2008 and the components of the Visual Basic language. You know how to design
graphical user interfaces (GUI) and how to use the statements of Visual Basic to program the
events of the various controls. You also know how to write functions and subroutines and how to
call the built-in functions and subroutines of Visual Basic.
In this chapter, you’ll design a few more Windows applications — this time, a few practical
applications with more functional interfaces and a bit of code that does something more practical.
You’ll put together the information presented so far in the book by building Windows applica-
tions with the visual tools of Visual Studio and you’ll see how the application interacts with users
by coding the events of interest. If you are new to Visual Studio, you should design the examples
on your own using the instructions in the text, rather than open the same projects and look at
the code.
In this chapter, you will learn how to do the following:
◆ Design graphical user interfaces
◆ Program events
◆ Write robust applications with error handling
OnDesigningWindowsApplications
As you recall from Chapter 1, ‘‘Getting Started with Visual Basic 2008,’’ the design of a Windows
application consists of two distinct phases: the design of the application’s interface and the cod-
ing of the application. The design of the interface is performed with visual tools and consists of
creating a form with the relevant elements. These elements are the building blocks of Windows
applications and are called controls.
The available controls are shown in the Toolbox and are the same elements used by all Win-
dows applications. In addition to being visually rich, the controls embed a lot of functionality. The
TextBox control, for example, can handle text on its own, without any programming effort on your
part. The ComboBox control expands the list with its items when users click the arrow button and
displays the selected item in its edit box. In general, the basic functionality of the controls is built
into the controls by design, so that all applications maintain a consistent look.Petroutsos c04.tex V3 - 01/28/2008 12:36pm Page 124
124 CHAPTER 4 GUI DESIGN AND EVENT-DRIVEN PROGRAMMING
The interface dictates how users will interact with your application. To prompt users for text or
numeric data, use TextBox controls. When it comes to specifying one or more of several options,
you have many choices: You can use a ComboBox control from which users can select an option,
or a few CheckBox controls on the form that users can select or clear. If you want to display a small
number of mutually exclusive options, place a few RadioButton controls on the form. Every time
the user selects an option, the previously selected one is cleared. To initiate actions, place one or
more Button controls on the form.
Controls expose a large number of properties, which are displayed in the Properties window at
design time. You use these properties to adjust not only the appearance of the controls on the form,
but their functionality as well. The process of designing the interface consists mostly of setting the
properties of the various controls.
An important aspect of the design of your application’s user interface is the alignment of the
controls on the form. Controls that are next to one another should be aligned horizontally. Controls
that are stacked should have either their left or right edges aligned vertically. You should also
make sure that the controls are spaced equally. The integrated development environment (IDE)
provides all the tools for sizing, aligning, and spacing controls on the form, and you’ll see these
tools in action through examples in this chapter.
After you have designed the interface, you know how your application will interact with the
user. The next step is to actually implement the interaction by writing some code. The program-
ming model of Visual Basic is event-driven: As the user interacts with the controls on your form,
some code is executed in response to user actions. The user’s actions cause events, and each con-
trol recognizes its own set of events and handles them through subroutines, which are called event
handlers.When users click a button, the control’s Click event is fired, and you must insert the rele-
vant code in the control’s Click event handler. The event-driven programming model has proven
very successful, because it allows developers to focus on handling specific actions. It allows you to
break a large application into smaller, manageable units of code and implement each unit of code
independently of any other.
Developing Windows applications is a conceptually simple process, but there’s a methodol-
ogy to it and it’s not trivial. Fortunately, the IDE provides many tools to simplify the process;
it will even catch most of the errors in your code as you type. You have seen how to use some
of the tools of the IDE in the first three chapters. In this chapter, I’ll present these tools through
examples.
Building a Loan Calculator
One easy-to-implement, practical application is a program that calculates loan parameters. Visual
Basic provides built-in functions for performingmany types of financial calculations, and you need
only a single line of code to calculate the monthly payment given the loan amount, its duration,
and the interest rate. Designing the user interface, however, takes much more effort.
Regardless of the language you use, you must go through the following process to develop an
application:
1. Decide what the application will do and how it will interact with the user.
2. Design the application’s user interface according to the requirements of step 1.
3. Write the actual code behind the events you want to handle.Petroutsos c04.tex V3 - 01/28/2008 12:36pm Page 125
BUILDING A LOAN CALCULATOR 125
Understanding How the Loan Calculator Application Works
Following the first step of the process outlined previously, you decide that the user should be able
to specify the amount of the loan, the interest rate, and the duration of the loan in months. You
must, therefore, provide three text boxes in which the user can enter these values.
Another parameter affecting the monthly payment is whether payments are made at the begin-
ning or at the end of each month, so you must also provide a way for the user to specify whether
the payments will be early (first day of the month) or late (last day of the month). The most appro-
priate type of control for entering Yes/No or True/False type of information is the CheckBox
control. This control is a toggle: If it’s selected, you can clear it by clicking it; if it’s cleared, you can
select it by clicking again. The user doesn’t enter any data in this control (which means you need
not anticipate user errors with this control), and it’s the simplest method for specifying values
with two possible states.
Figure 4.1 shows a user interface that matches our design specifications. This is the main form
of the LoanCalculator project, which you will find in this chapter’s folder on the book’s project
download site.
Figure 4.1
LoanCalculator is a sim-
ple financial application.
The user enters all the information on the form and then clicks the Monthly Payment button to
calculate the monthly payment. The program will calculate the monthly payment and display it in
the lower TextBox control. All the action takes place in the button’s Click subroutine.
To calculate the monthly payments on a loan, we call the Pmt () built-in function, whose syntax
is the following:
MonthlyPayment = Pmt(InterestRate, Periods, Amount, FutureValue, Due)
The interest rate, argument InterestRate, is specified as a monthly rate. If the yearly inter-
est rate is 16.5 percent, the value entered by the user in the Interest Rate box should be 14.5,
and the monthly rate will be 0.145/12. The duration of the loan, the Periods argument, isPetroutsos c04.tex V3 - 01/28/2008 12:36pm Page 126
126 CHAPTER 4 GUI DESIGN AND EVENT-DRIVEN PROGRAMMING
specified in number of months, and the Amount argument is the loan’s amount. The FutureValue
argument is the value of the loan at the end of the period, which should be zero (it would be a
positive value for an investment), and the last argument, Due, specifies when payments are due.
The value of Due can be one of the constants DueDate.BegOfPeriod and DueDate.EndOfPeriod.
These two constants are built into the language, and you can use them without knowing their
exact value.
The present value of the loan is the amount of the loan with a negative sign. It’s negative
because you don’t have the money now. You’re borrowing it — it is money you owe to the bank.
Future value represents the value of something at a stated time — in this case, what the loan will
be worth when it’s paid off. This is what one side owes the other at the end of the specified period.
So the future value of a loan is zero.
You don’t need to know how the Pmt () function calculates the monthly payment, just how
to call it and how to retrieve the results. To calculate the monthly payment on a loan of $25,000
with an interest rate of 14.5 percent, payable over 48 months, and payments due the last day
of the payment period (which in our case is a month), you’d call the Pmt() function as
follows:
Pmt(0.145 / 12, 48, -25000, 0, DueDate.EndOfPeriod)
The Pmt() function will return the value 689.448821287218. Because it’s a dollar amount, we
must round it to two decimal digits on our interface.Notice the negative sign in front of the Amount
argument in the statement. If you specify a positive amount, the result will be a negative payment.
The payment and the loan’s amount have different signs because they represent different cash
flows. The loan’s amount is money you owe to the bank, whereas the payment is money you pay
to the bank.
The last two arguments of the Pmt() function are optional. If you omit them, Visual Basic uses
their default values, which are 0 for the FutureValue argument and DueDate.BegOfPeriod for
the Due argument. You can entirely omit these arguments and call the Pmt() function like this:
Pmt(0.145 / 12, 48, -25000)
Calculating the amount of the monthly payment given the loan parameters is quite
simple. What you need to understand are the parameters of a loan and how to pass them to
the Pmt() function. You must also know how the interest rate is specified to avoid invalid val-
ues. Although the calculation of the payment is trivial, designing the interface will take a bit
of effort.
Designing the User Interface
Now that you know how to calculate the monthly payment, you can design the user interface. To
do so, start a new project, name it LoanCalculator, and rename its form to frmLoan.Yourfirst
task is to decide the font and size of the text you’ll use for the controls on the form. The form is
the container of the controls, and they inherit some of the form’s properties, such as the font. You
can change the font later during the design, but it’s a good idea to start with the right font. At anyPetroutsos c04.tex V3 - 01/28/2008 12:36pm Page 127
BUILDING A LOAN CALCULATOR 127
rate, don’t try to align the controls if you’re planning to change their fonts. The change will, most
likely, throw off your alignment efforts.
The book’s sample project uses the 10-point Verdana font. To change it, select the form with
the mouse, double-click the name of the Font property in the Properties window to open the Font
dialog box, and select the desired font and attributes. I’m using the Verdana and Seago fonts a lot
because they’re clean and theywere designed for viewing onmonitors. Of course, this is a personal
choice. Avoid elaborate fonts and don’t mix different fonts on the same form (or in different forms
of the same application).
To design the form shown in Figure 4.1, follow these steps:
1. Place four labels on the form and assign the following captions (the Text property of each
control) to them:
Name Text
Label1 Amount
Label2 Duration
Label3 Interest Rate
Label4 Monthly Payment
You don’t need to change the default names of the four Label controls on the form because
their captions are all we need. You aren’t going to program them.
2. Place a TextBox control next to each label. Set their Name and Text properties to the follow-
ing values. I used meaningful names for the TextBox controls because we’ll use them in our
code shortly to retrieve the values entered by the user on these controls. These initial values
correspond to a loan of $25,000 with an interest rate of 14.5 percent and a payoff period of
48 months.
Name Text
txtAmount 25000
txtDuration 48
txtRate 14.5
txtPayment
3. The fourth TextBox control is where the monthly payment will appear. The user isn’t
supposed to enter any data in this box, so youmust set its ReadOnly property to True to lock
the control. You’ll be able to change its value fromwithin your code, but users won’t be able
to type anything in it. (We could have used a Label control instead, but the uniform look of
TextBoxes on a formis usually preferred.) Youwill also notice that the TextBox controls have
a 3D frame. Experiment with the control’s BorderStyle property to discover the available
styles for the control’s frame (I’ve used the Fixed3D setting for the TextBox controls).Petroutsos c04.tex V3 - 01/28/2008 12:36pm Page 128
128 CHAPTER 4 GUI DESIGN AND EVENT-DRIVEN PROGRAMMING
4. Next, place a CheckBox control on the form. By default, the control’s caption is CheckBox1,
and it appears to the right of the check box. Because we want the titles to be to the left of the
corresponding controls, we’ll change this default appearance.
5. Select the check box with the mouse, and in the Properties window locate the CheckAlign
property. Its value is MiddleLeft. If you expand the drop-down list by clicking the arrow
button, you’ll see that this property has many different settings, and each setting is shown
as a square. Select the button that will center the text vertically and right-align it hori-
zontally. The string MiddleRight will appear in the Properties window when you click the
appropriate button.
6. With the check box selected, locate the Name property in the Properties window, and set it
to chkPayEarly.
7. Change the CheckBox’s caption by entering the string Early Payment in its Text property
field.
8. Place a Button control in the bottom-left corner of the form. Name it bttnShowPayment,and
set its Text property to Monthly Payment.
9. Finally, place another Button control on the form, name it bttnExit,andsetits Text prop-
erty to Exit.Petroutsos c04.tex V3 - 01/28/2008 12:36pm Page 129
BUILDING A LOAN CALCULATOR 129
Aligning the Controls
Your next step is to align the controls on the form. The IDE provides commands to align the
controls on the form, all of which can be accessed through the Format menu. To align the controls
that are already on the form, follow these steps:
1. Select the four labels on the form. The handles of all selected controls will be black, except
for one control whose handles will be white. To specify the control that will be used as a
reference for aligning the other controls, click it after making the selection. (You can select
multiple controls either by drawing a rectangle that encloses them with the mouse, or by
clicking each control while holding down the Ctrl button.)
2. With the four text boxes selected, choose Format  Align  Left to left-align them. Don’t
include the check box in this selection.
3. Resize the CheckBox control. Its left edge should align with the left edges of the Label
controls, and its right edge should align with the right edges of the Label controls.
4. Select all the Labels and the CheckBox controls and choose Format  Vertical Spacing
 Make Equal. This action will space the controls vertically. Then align the baseline of
each TextBox control with the baseline of the matching Label control. To do so, move each
TextBox control with the mouse until you see a magenta line that connects the baseline
of the TextBox control you’re moving and that of the matching Label control.
Your form should now look like the one shown in Figure 4.1. Take a good look at it and check
to see whether any of your controls are misaligned. In the interface design process, you tend
to overlook small problems such as a slightly misaligned control. The user of the application,
however, instantly spots such mistakes.
Programming the Loan Application
Now that you’ve created the interface, run the application and see how it behaves. Enter a few
values in the text boxes, change the state of the check box, and test the functionality already built
into the application. Clicking the Monthly Payment button won’t have any effect because we
have not yet added any code. If this were a prototype you were building for a customer, you
would add a statement in the Monthly Payment button to display a random value in the Monthly
Payment box. The purpose of the prototype is to get the customer’s approval on the appearance
and functionality of an application before you start coding it.
If you’re happywith the user interface, stop the application, open the form, and double-click the
Monthly Payment Button control. Visual Basic opens the code window and displays the definition
of the ShowPayment Click event:
Private Sub bttnShowPayment Click(...)
Handles bttnShowPayment.Click
End Sub
Because all Click event handlers have the same signature (they provide the same two argu-
ments), I’ll be omitting the list of arguments from now on. Actually, all event handlers have two
arguments, and the first of them is always the control that fired the event. The type of the secondPetroutsos c04.tex V3 - 01/28/2008 12:36pm Page 130
130 CHAPTER 4 GUI DESIGN AND EVENT-DRIVEN PROGRAMMING
argument differs depending on the type of the event. Place the pointer between the lines Private
Sub and End Sub, and enter the rest of the lines of Listing 4.1. (You don’t have to reenter the first
and last lines that declare the event handler.)
Listing 4.1: The Code behind theMonthly Payment Button
Private Sub bttnShowPayment Click(...)
Handles bttnShowPayment.Click
Dim Payment As Double
Dim LoanIRate As Double
Dim LoanDuration As Integer
Dim LoanAmount As Integer
LoanAmount = Convert.ToInt32(txtAmount.Text)
LoanIRate = 0.01 * Convert.ToDecimal(txtRate.Text) / 12
LoanDuration = Convert.ToInt32(txtDuration.Text)
Dim payEarly As DueDate
If chkPayEarly.Checked Then
payEarly = DueDate.BegOfPeriod
Else
payEarly = DueDate.EndOfPeriod
End If
Payment = Pmt(LoanIRate, LoanDuration, -LoanAmount, 0, payEarly)
txtPayment.Text = Payment.ToString(”#.00”)
End Sub
The code window should now look like the one shown in Figure 4.2. Notice the underscore
character at the end of the first part of the long line. The underscore lets you break long lines so
that they will fit nicely in the code window. I’m using this convention in this book a lot to fit long
lines on the printed page. The same statement you see as multiple lines in the book may appear in
a single, long line in the project.
You don’t have to break long lines manually as you enter code in the editor’s window.
Open the Edit menu and choose Advanced  Word Wrap. The editor will wrap long lines auto-
matically at a word boundary. While the word wrap feature is on, a check mark appears in front
of the Edit  Advanced  Word Wrap command. To turn off word wrapping, select the same
command again.
In Listing 4.1, the first line of code within the subroutine declares a variable. It lets the applica-
tion know that Payment is a variable for storing a floating-point number (a number with a decimal
part)— the Double data type. The line before the If statement declares a variable of the DueDate
type. This is the type of the argument that determines whether the payment takes place at the
beginning or the end of the month. The last argument of the Pmt() function must be a variable
of this type, so we declare a variable of the DueDate type. As mentioned earlier in this chapter,
DueDate is an enumeration with two members: BegOfPeriod and EndOfPeriod.
The first really interesting statement in the subroutine is the If statement that examines the
value of the chkPayEarly CheckBox control. If the control is selected, the code sets the payEarly
variable to DueDate.BegOfPeriod. If not, the code sets the same variable to DueDate.EndOfPeriod.Petroutsos c04.tex V3 - 01/28/2008 12:36pm Page 131
BUILDING A LOAN CALCULATOR 131
Figure 4.2
The Show Payment
button’s Click event
subroutine.
The ComboBox control’s Checked property returns True if the control is selected at the time, and
returns False otherwise. After setting the value of the payEarly variable, the code calls the Pmt()
function, passing the values of the controls as arguments:
◆ The first argument is the interest rate. The value entered by the user in the txtRate TextBox
is multiplied by 0.01 so that the value 14.5 (which corresponds to 14.5 percent) is passed to
the Pmt() function as 0.145. Although we humans prefer to specify interest rates as inte-
gers (8 percent) or floating-point numbers larger than 1 (8.24 percent), the Pmt() function
expects to read a number less than 1. The value 1 corresponds to 100 percent. Therefore, the
value 0.1 corresponds to 10 percent. This value is also divided by 12 to yield the monthly
interest rate.
◆ The second argument is the duration of the loan in months (the value entered in the
txtDuration TextBox).
◆ The third argument is the loan’s amount (the value entered in the txtAmount TextBox).
◆ The fourth argument (the loan’s future value) is 0 by definition.
◆ The last argument is the payEarly variable, which is set according to the status of the chk-
PayEarly control.
The last statement in Listing 4.1 converts the numeric value returned by the Pmt() function to a
string and displays this string in the fourth TextBox control. The result is formatted appropriately
with the following expression:
Payment.ToString(”#.00”)
The Payment variable is numeric, and all numeric variables provide the method ToString,
which formats the numeric value and converts it to a string. The character # stands for the integer
part of the variable. The period separates the integer from the fractional part, which is roundedPetroutsos c04.tex V3 - 01/28/2008 12:36pm Page 132
132 CHAPTER 4 GUI DESIGN AND EVENT-DRIVEN PROGRAMMING
to two decimal digits. The Pmt() function returns a precise number, such as 372.2235687646345,
and you must round it to two decimal digits and format it nicely before displaying it. For more
information on formatting numeric (and other) values, see the section ‘‘Formatting Numbers’’
in Chapter 2, ‘‘Variables and Data Types.’’ Finally, the formatted string is assigned to the Text
property of the TextBox control on the form.
A Code Snippet for Calculating Monthly Loan Payments
If you didn’t know about the Pmt() built-in function, how would you go about calculating loan pay-
ments? Code snippets to the rescue! Right-click somewhere in the code window and from the con-
text menu choose the Insert Snippet command. Double-click the fundamentals to see another list of
items. This time double-click the Math folder and then select the snippet Calculate a Monthly Payment
on a Loan. The following code will be inserted at the location of the pointer (I’ve broken the last long
statement into two lines to fit it on the printed page):
Dim futureValue As Double = 0
Dim payment As Double
payment1 = Pmt(0.05 / 12, 36, -1000, futureValue, DueDate.EndOfPeriod)
The snippet demonstrates the use of the Pmt() function. All you have to do is replace the values of
the various parameters with the data from the appropriate controls on the form.
If you don’t know how to use the arguments of the Pmt() function, rest the pointer over each argu-
ment and you will see a description for each argument, as shown here:
The code of the LoanCalculator sample project is a bit different and considerably longer
than what I have presented here. The statements discussed in the preceding text are the bare
minimum for calculating a loan payment. The user can enter all kinds of unreasonable values
on the form and cause the program to crash. In the next section, you’ll see how you can validate
the data entered by the user, catch errors, and handle them gracefully (that is, give the user a
chance to correct the data and proceed), as opposed to terminating the application with a run-
time error.Petroutsos c04.tex V3 - 01/28/2008 12:36pm Page 133
BUILDING A LOAN CALCULATOR 133
Validating the Data
If you enter a non-numeric value in one of the fields, the program will crash and display an error
message. For example, if you enter twenty in the Duration text box, the program will display the
error message shown in Figure 4.3. A simple typing error can crash the program. This isn’t the
way Windows applications should work. Your applications must be able to handle all kinds of
user errors, provide helpful messages, and in general, guide the user in running the application
efficiently. If a user error goes unnoticed, your application will either end abruptly or will produce
incorrect results without an indication.
Figure 4.3
The FormatException
error message means
that you supplied a
string where a numeric
value was expected.
Visual Basic will take you back to the application’s code window, in which the statements that
caused the error will be highlighted in green. Obviously, we must do something about user errors.
One way to take care of typing errors is to examine each control’s contents; if the controls don’t
contain valid numeric values, display your own descriptive message and give the user another
chance. Listing 4.2 is the revised Click event handler that examines the value of each text box
before attempting to use it in any calculations.
Listing 4.2: Revised Show Payment Button
Private Sub bttnShowPayment Click(ByVal sender As System.Object,
ByVal e As System.EventArgs) Handles bttnShowPayment.Click
Dim Payment As Double
Dim LoanIRate As Double
Dim LoanDuration As Integer
Dim LoanAmount As Integer
’ Validate amount
If IsNumeric(txtAmount.Text) Then
LoanAmount = Convert.ToInt32(txtAmount.Text)
Else
MsgBox(”Please enter a valid amount”)
Exit Sub
End If
’ Validate interest rate
If IsNumeric(txtRate.Text) Then
LoanIRate = 0.01 * Convert.ToDouble(txtRate.Text) / 12
ElsePetroutsos c04.tex V3 - 01/28/2008 12:36pm Page 134
134 CHAPTER 4 GUI DESIGN AND EVENT-DRIVEN PROGRAMMING
MsgBox(”Invalid interest rate, please re-enter”)
Exit Sub
End If
’ Validate loan’s duration
If IsNumeric(txtDuration.Text) Then
LoanDuration = Convert.ToInt32(txtDuration.Text)
Else
MsgBox(”Please specify the loan’s duration as a number of months”)
Exit Sub
End If
’ If all data were validated, proceed with calculations
Dim payEarly As DueDate
If chkPayEarly.Checked Then
payEarly = DueDate.BegOfPeriod
Else
payEarly = DueDate.EndOfPeriod
End If
Payment = Pmt(LoanIRate, LoanDuration, -LoanAmount, 0, payEarly)
txtPayment.Text = Payment.ToString(”#.00”)
End Sub
First, we declare three variables in which the loan’s parameters will be stored: LoanAmount,
LoanIRate,and LoanDuration. These values will be passed to the Pmt() function as arguments.
Each text box’s value is examined with an If structure. If the corresponding text box holds a valid
number, its value is assigned to the numeric variable. If not, the program displays a warning
and exits the subroutine without attempting to calculate the monthly payment. Before exiting the
subroutine, however, the code moves the focus to the text box with the invalid value because this
is the control that the user will most likely edit. After fixing the incorrect value, the user can click
the Show Payment button again. IsNumeric() is another built-in function that accepts a variable
and returns True if the variable is a number, and returns False otherwise.
You can run the revised application and check it out by entering invalid values in the fields.
Notice that you can’t specify an invalid value for the last argument; the CheckBox control won’t let
you enter a value. You can only select or clear it, and both options are valid. The actual calculation
of the monthly payment takes a single line of Visual Basic code. Displaying it requires another
line of code. Adding the code to validate the data entered by the user, however, is an entire pro-
gram. And that’s the way things are.
WritingWell-Behaved Applications
A well-behaved application must contain data-validation code. If an application such as LoanCalcu-
lator crashes because of a typing mistake, nothing really bad will happen. The user will try again or
else give up on your application and look for a more professional one. However, if the user has been
entering data for hours, the situation is far more serious. It’s your responsibility as a programmer to
make sure that only valid data are used by the application and that the application keeps working, no
matter how the user misuses or abuses it.
The amount of code you write to validate user input is comparable to the amount of code that pro-
duces the results. Our sample application is not typical, because it calculates the result with a singlePetroutsos c04.tex V3 - 01/28/2008 12:36pm Page 135
BUILDING A LOAN CALCULATOR 135
function call, but in developing typical business applications, you must write a substantial amount
of code to validate user input. The reason for validating user input is that you should provide specific
error messages to help the user identify the error and correct it.
The applications in this book don’t contain much data-validation code because it would obscure the
‘‘useful’’ code that applies to the topic at hand. Instead, they demonstrate specific techniques. You
can use parts of the examples in your applications, but you should provide your own data-validation
code (and error-handling code, as you’ll see in a moment).
Now run the application one last time and enter an enormous loan amount. Try to find out
what it would take to pay off the national debt with a reasonable interest rate in, say, 72 months.
The program will crash again (as if you didn’t know). This time the program will go down with
a different error message, as shown in Figure 4.4. Visual Basic will complain about an overflow.
The exact message is Value was either too large or too small for an Int32, and the program will stop at
the line that assigns the contents of the txtAmount TextBox to the LoanAmount variable. Press the
Break button, and the offending statement in the code will be highlighted.
Figure 4.4
Very large values can
cause the application to
crash and display this
error message.
An overflow is a numeric value too large for the program to handle. This error is usually pro-
duced when you divide a number by a very small value. When you attempt to assign a very large
value to an Integer variable, you’ll also get an overflow exception.
Actually, in the LoanCalculator application, any amount greater than 2,147,483,647 will cause
an overflow condition. This is the largest value you can assign to an Integer variable; it’s plenty
for our banking needs, but not nearly adequate for handling government deficits. As you’ll see
in the next chapter, Visual Basic provides other types of variables, which can store enormous
values (making the national debt look really small). In the meantime, if you want to use the loan
calculator, change the declaration of the LoanAmount variable to the following:
Dim LoanAmount As DoublePetroutsos c04.tex V3 - 01/28/2008 12:36pm Page 136
136 CHAPTER 4 GUI DESIGN AND EVENT-DRIVEN PROGRAMMING
The Double data type can hold much larger values. Besides, the Double data type can also hold
noninteger values. Not that anyone will ever apply for a loan of $25,000 and some cents, but if you
want to calculate the precise monthly payment for a debt you have accumulated, you should be
able to specify a noninteger amount. In short, we should have declared the LoanAmount variable
with the Double data type in the first place. By the way, there’s another integer data type, the Long
data type, which can hold much larger integer values.
An overflow error can’t be caught with data-validation code. There’s always a chance that your
calculations will produce overflows or other types of math errors. Data validation won’t help
here; you just don’t know the result before you carry out the calculations. We need something
called error handling,or exception handling. This is additional code that can handle errors after they
occur. In effect, you’re telling VB that it shouldn’t stop with an error message, which would be
embarrassing for you and wouldn’t help the user one bit. Instead, VB should detect the error
and execute the proper statements that will handle the error. Obviously, you must supply these
statements. (You’ll see examples of handling errors at runtime shortly.)
The sample application works as advertised and it’s fail-safe. Yet there’s one last touch we can
add to our application. The various values on the form are not always in synch. Let’s say you’ve
calculated the monthly payment for a specific loan and then you want to change the duration of
the loan to see how it affects themonthly payment. As soon as you change the duration of the loan,
and before you click the Monthly Payment button, the value in the Monthly Payment box doesn’t
correspond to the parameters of the loan. Ideally, the monthly payment should be cleared as
soon as the user starts editing one of the loan’s parameters. To do so, you must insert a statement
that clears the txtPayment control. But what’s the proper event handler for this statement? The
TextBox control fires the TextChanged event every time its text is changed, and this is the proper
place to execute the statement that clears themonthly payment on the form. Because there are three
TextBox controls on the form, you must program the TextChanged event of all three controls, or
write an event handler that handles all three events:
Private Sub txtAmount TextChanged(...)
Handles txtAmount.TextChanged,
txtDuration.TextChanged, txtRate.TextChanged
txtPayment.Clear()
End Sub
Yes, you can write a common handler for multiple events, as long as the events are of the same
type and they’re all listed after the Handles keyword. You’ll see another example of the same
technique in the following sample project.
One of the sample projects for this chapter is a revised version of the LoanCalculator project, the
LoanCalculator-Dates project, which uses a different interface. Instead of specifying the duration
of the loan in months, this application provides two instances of the DateTimePicker control,
which is used to specify dates. Delete the TextBox control and the corresponding Labels and
insert two new Labels and two DateTimePicker controls on the form. Users can set the loan’s
starting and ending dates on these two controls and the program calculates the duration of the
loan in moths with the following statement:
LoanDuration = DateDiff(DateInterval.Month,
dtFrom.Value, dtTo.Value) + 1
dtFrom and dtTo are the names of the two DateTimePicker controls. The DateDiff() function
returns the difference between two dates in the interval supplier as the first argument to thePetroutsos c04.tex V3 - 01/28/2008 12:36pm Page 137
BUILDING A CALCULATOR 137
function. The rest of the code doesn’t change; as long as the LoanDuration variable has the correct
value, the same statements will produce the correct result. If you open the project you’ll find a few
more interesting statements that set the dtFrom control to the first date of the selected month and
the dtTo control to the last date of the selected month.
Building a Calculator
Our next application is more advanced, but not as advanced as it looks. It’s a calculator with
a typical visual interface that demonstrates how Visual Basic can simplify the programming of
fairly advanced operations. If you haven’t tried it, you may think that writing an application such
as this one is way too complicated for a beginner, but it isn’t. The MathCalculator application is
shown in Figure 4.5.
Figure 4.5
Calculator application
window
The application emulates the operation of a hand-held calculator and implements the basic
arithmetic operations. It has the look of a math calculator, and you can easily expand it by adding
more features. In fact, adding features such as cosines and logarithms is actually simpler than
performing the basic arithmetic operations. This interface will also give us a chance to exercise
most of the tools of the IDE for aligning and spacing the controls on a form.
Designing the User Interface
The application’s interface is straightforward, but it takes a bit of effort. You must align the but-
tons on the form and make the calculator look as much like a hand-held calculator as possible.
Start a new project, the MathCalculator project, and rename its main form from Form1.vb to
frmCalculator.vb.
Designing the interface of the application isn’t trivial because it’s made up of many buttons, all
perfectly aligned on the form. To simplify the design, follow these steps:
1. Select a font that you like for the form. All the command buttons you’ll place on the form
will inherit this font. The MathCalculator sample application uses 10-point Verdana font.
I’ve used a size of 12 points for the Period button, because the 10-point period was too
small and very near the bottom of the control.
2. Add the Label control, which will become the calculator’s display. Set its BorderStyle
property to Fixed3D so that it will have a 3D look, as shown in Figure 4.5. Change itsPetroutsos c04.tex V3 - 01/28/2008 12:36pm Page 138
138 CHAPTER 4 GUI DESIGN AND EVENT-DRIVEN PROGRAMMING
ForeColor and BackColor properties too, if you want it to look different from the rest
of the form. The sample project uses colors that emulate the— now extinct— green CRT
monitors.
3. Draw a Button control on the form, change its Text property to 1, and name it bttn1.Size
the button carefully so that its caption is centered on the control. The other buttons on the
form will be copies of this one, so make sure you’ve designed the first button as best as
you can before you start making copies of it. You can also change the button’s style with
the FlatStyle property. (You can experiment with the Popup, Standard, and System set-
tings of this property.)
4. Place the button in its final position on the form. At this point, you’re ready to create the
other buttons for the calculator’s digits. Right-click the button and choose Copy from the
context menu. The Button control is copied to the Clipboard, and now you can paste it on
the form (which is much faster than designing an identical button).
5. Right-click somewhere on the form, choose Paste, and the button copied to the Clipboard
will be pasted on the form. The copy will have the same caption as the button it was
copied from, and its name will be Button1.
6. Now set the button’s Name to bttn2 and its Text property to 2. This button is the digit 2.
Place the new button to the right of the previous button. You don’t have to align the two
buttons perfectly now; later we’ll use the Format menu to align the buttons on the form.
As you move the control around on the form, one or more lines may appear at times.
These lines are called snap lines, and they appear as soon as a control is aligned (verti-
cally or horizontally) with one or more of the existing controls on the form. The snap lines
allow you to align controls with the mouse. Blue snap lines appear when the control’s
edge is aligned with the edge of another control. Red snap lines appear when the control’s
baseline is aligned with the baseline of another control. The baseline is the invisible line on
which the characters of the control’s caption are based.
7. Repeat steps 5 and 6 eight more times, once for each numeric digit. Each time a new
Button control is pasted on the form, Visual Basic names it Button1 and sets its caption
to 1; you must change the Name and Text properties. You can name the buttons any-
thing you like, but a name that indicates their role in the application is preferred.
8. When the buttons of the numeric digits are all on the form, place two more buttons, one
for the C (Clear) operation and one for the Period button. Name them bttnClear and
bttnPeriod, and set their captions accordingly. Use a larger font size for the Period but-
ton to make its caption easier to read.
9. When all the digit buttons of the first group are on the form and in their approximate posi-
tions, align them by using the commands of the Format menu. You can use the snap lines
to align horizontally and vertically the various buttons on the form, but you must still
space the controls manually, which isn’t a trivial task. Here’s how you can align the but-
tons perfectly via the Format menu:
a. First, align the buttons of the top row. Start by aligning the 1 button with the left side
of the lblDisplay Label. Then select all the buttons of the top row and make their
horizontal spacing equal (choose Format  Horizontal Spacing  Make Equal). Then
do the same with the buttons in the first column; this time, make sure that their vertical
distances are equal (Format  Vertical Spacing  Make Equal).Petroutsos c04.tex V3 - 01/28/2008 12:36pm Page 139
BUILDING A CALCULATOR 139
b. Now you can align the buttons in each row and each column separately. Use one
of the buttons you aligned in the last step as the guide for the rest of them. The but-
tons can be aligned in many ways, so don’t worry if somewhere in the process you
ruin the alignment. You can always use the Undo command in the Edit menu. Select
the three buttons on the second row and align their Tops by using the first button
as a reference. To set the anchor control for the alignment, click it with the mouse
while holding down the Ctrl key. Do the same for the third and fourth rows of but-
tons. Then do the same for the four columns of buttons, using the top button as a
reference.
10. Now, place the buttons for the arithmetic operations on the form— addition (+), subtrac-
tion (−), multiplication (*), and division (/).
11. Finally, place the Equals button on the form and make it wide enough to span the space of
two operation buttons. Use the commands on the Format menu to align these buttons,
as shown in Figure 4.5. The form shown in Figure 4.5 has a few more buttons, which
you can align by using the same techniques you used to align the numeric buttons.
If you don’t feel quite comfortable with the alignment tools of the IDE, you can still position
the controls on the form through the x and y components of each control’s Location property.
(They’re the x- and y-coordinates of the control’s upper-left corner on the form.) The various
alignment tools are among the first tools of the IDE you’ll master, and you’ll be creating forms
with perfectly aligned controls in no time at all.
Programming the MathCalculator
Now you’re ready to add some code to the application. Double-click one of the digit buttons on
the form, and you’ll see the following in the code window:
Private Sub bttn1 Click(ByVal sender As System.Object,
ByVal e As System.EventArgs) Handles bttn1.Click
End Sub
This is the Click event’s handler for a single digit button. Your first attempt is to program
the Click event handler of each digit button, but repeating the same code 10 times isn’t very
productive. (Not to mention that if we decide to edit the code later, the process must be repeated
10 times.) We’re going to use the same event handler for all buttons that represent digits. All
you have to do is append the names of the events to be handled by the same subroutine after
the Handles keyword. You should also change the name of the event handler to something that
indicates its role. Because this subroutine handles the Click event for all the digit buttons, let’s
call it DigitClick(). Here’s the revised declaration of a subroutine that can handle all the digit
buttons:
Private Sub DigitClick(ByVal sender As System.Object,
ByVal e As System.EventArgs)
Handles bttn0.Click, bttn1.Click, bttn2.Click,
bttn3.Click, bttn4.Click, bttn5.Click, bttn6.Click,
bttn7.Click, bttn8.Click, bttn9.Click
End SubPetroutsos c04.tex V3 - 01/28/2008 12:36pm Page 140
140 CHAPTER 4 GUI DESIGN AND EVENT-DRIVEN PROGRAMMING
You don’t have to type all the event names; as soon as you insert the first comma after
bttn0.Click, a drop-down list with the names of the controls will open, and you can select the
name of the next button with the down arrow. Press the spacebar to select the desired control
(bttn1, bttn2, and so on), and then type the period. This time, you’ll see another list with the
names of the events for the selected control. Locate the Click event and select it by pressing the
spacebar. Type the next comma and repeat the process for all the buttons. This extremely conve-
nient feature of the language is IntelliSense: The IDE presents the available and valid keywords as
you type.
When you press a digit button on a hand-held calculator, the corresponding digit is appended
to the display. To emulate this behavior, insert the following line in the Click event handler:
lblDisplay.Text = lblDisplay.Text + sender.Text
This line appends the digit clicked to the calculator’s display. The sender argument of the
Click event represents the control that was clicked (the control that fired the event). The Text
property of this control is the caption of the button that was clicked. For example, if you have
already entered the value 345, clicking the digit 0 displays the value 3450 on the Label control that
acts as the calculator’s display.
The expression sender.Text is not the bestmethod of accessing the Text property of the button
that was clicked, but it will work as long as the Strict option is off. As discussed in Chapter 2, we
must cast the sender object to a specific type (the Button type) and then call its Text method:
CType(sender, Button).Text
The code behind the digit buttons needs a few more lines. After certain actions, the display
should be cleared. After pressing one of the buttons that correspond to math operations, the dis-
play should be cleared in anticipation of the second operand. Actually, the display must be cleared
as soon as the first digit of the second operand is pressed, and not as soon as the math operator
button is pressed. Likewise, the display should also be cleared after the user clicks the Equals
button. Revise the DigitClick event handler, as shown in Listing 4.3.
Listing 4.3: The DigitClick Event
Private Sub DigitClick(ByVal sender As System.Object,
ByVal e As System.EventArgs)
Handles bttn1.Click, bttn2.Click, bttn3.Click,
bttn4.Click, bttn5.Click, bttn6.Click,
bttn7.Click, bttn8.Click, bttn9.Click
If clearDisplay Then
lblDisplay.Text = ””
clearDisplay = False
End If
lblDisplay.Text = lblDisplay.Text + sender.text
End SubPetroutsos c04.tex V3 - 01/28/2008 12:36pm Page 141
BUILDING A CALCULATOR 141
The clearDisplay variable is declared as Boolean, which means it can take a True or False
value. Suppose that the user has performed an operation and the result is on the calculator’s
display. The user now starts typing another number. Without the If clause, the program would
continue to append digits to the number already on the display. This is not how calculators work.
When the user starts entering a new number, the display must be cleared. And our program uses
the clearDisplay variable to know when to clear the display.
The Equals button sets the clearDisplay variable to True to indicate that the display contains
the result of an operation. The DigitClick() subroutine examines the value of this variable each
time a new digit button is pressed. If the value is True, DigitClick() clears the display and then
prints the new digit on it. The subroutine also sets clearDisplay to False so that when the next
digit is pressed, the program won’t clear the display again.
What if the user makes a mistake and wants to undo an entry? The typical hand-held calculator
has no Backspace key. The Clear key erases the current number on the display. Let’s implement
this feature. Double-click the C button and enter the code of Listing 4.4 in its Click event.
Listing 4.4: Programming the Clear Button
Private Sub bttnClear Click(ByVal sender As System.Object,
ByVal e As System.EventArgs) Handles bttnClear.Click
lblDisplay.Text = ””
End Sub
Now we can look at the Period button. A calculator, no matter how simple, should be able to
handle fractional numbers. The Period button works just like the digit buttons, with one excep-
tion. A digit can appear any number of times in a numeric value, but the period can appear only
once. A number such as 99.991 is valid, but you must make sure that the user can’t enter numbers
such as 23.456.55. After a period is entered, this button must not insert another one. The code in
Listing 4.5 accounts for this.
Listing 4.5: Programming the Period Button
Private Sub bttnPeriodClick(ByVal sender As System.Object,
ByVal e As System.EventArgs) Handles bttnPeriod.Click
If lblDisplay.Text.IndexOf(”.”) >= 0 Then
Exit Sub
Else
lblDisplay.Text = lblDisplay.Text & ”.”
End If
End Sub
IndexOf is a method that can be applied to any string. The expression lblDisplay.Text
is a string (the text on the Label control), so we can call its IndexOf method. The expression
lblDisplay.Text.IndexOf(‘‘.’’) returns the location of the first instance of the period in thePetroutsos c04.tex V3 - 01/28/2008 12:36pm Page 142
142 CHAPTER 4 GUI DESIGN AND EVENT-DRIVEN PROGRAMMING
caption of the Label control. If this number is zero or positive, the number entered contains a
period already, and another can’t be entered. In this case, the program exits the subroutine. If the
method returns −1, the period is appended to the number entered so far, just like a regular digit.
Check out the operation of the application. We have already created a functional user interface
that emulates a hand-held calculator with data-entry capabilities. It doesn’t perform any oper-
ations yet, but we have already created a functional user interface with only a small number of
statements.
Coding the Math Operations
Now we can move to the interesting part of the application: the coding of the math operations.
Let’s start by defining three variables:
Operand1 The first number in the operation
Operator The desired operation
Operand2 The second number in the operation
When the user clicks one of the math symbols, the value on the display is stored in the variable
Operand1. If the user then clicks the Plus button, the program must make a note to itself that the
current operation is an addition and set the clearDisplay variable to True so that the user can
enter another value (the second value to be added). The symbol of the operation is stored in the
Operator variable. The user enters another value and then clicks the Equals button to see the
result. At this point, our program must do the following:
1. Read the value on the display into the Operand2 variable.
2. Perform the operation indicated by the Operator variable with the two operands.
3. Display the result and set the clearDisplay variable to True.
The Equals button must perform the following operation:
Operand1 Operator Operand2
Suppose that the number on the display when the user clicks the Plus button is 3342. The user
then enters the value 23 and clicks the Equals button. The program must carry out the addition:
3342 + 23
If the user clicked the Division button, the operation is as follows:
3342 / 23
Variables are local in the subroutines in which they are declared. Other subroutines have no
access to them and can’t read or set their values. Sometimes, however, variables must be accessed
from many places in a program. The variables Operand1, Operand2,and Operator,aswellasthe
clearDisplay variable, must be accessed from within more than one subroutine, so they must bePetroutsos c04.tex V3 - 01/28/2008 12:36pm Page 143
BUILDING A CALCULATOR 143
declared outside any subroutine; their declarations usually appear at the beginning of the code
with the following statements:
Dim clearDisplay As Boolean
Dim Operand1 As Double
Dim Operand2 As Double
Dim Operator As String
These variables are called form-wide variables,orsimply form variables, because they are visible
from within any subroutine on the form. Let’s see how the program uses the Operator variable.
When the user clicks the Plus button, the program must store the value ‘‘+’’ in the Operator
variable. This takes place from within the Plus button’s Click event.
All variables that store numeric values are declared as variables of the Double type, which can
store values with the greatest possible precision. The Boolean type takes two values: True and
False. You have already seen how the clearDisplay variable is used.
With the variable declarations out of the way, we can now implement the operator buttons.
Double-click the Plus button and, in the Click event’s handler, enter the lines shown in Listing 4.6.
Listing 4.6: The Plus Button
Private Sub bttnPlus Click(...) Handles bttnPlus.Click
Operand1 = Convert.ToDouble(lblDisplay.Text)
Operator = ”+”
clearDisplay = True
End Sub
The variable Operand1 is assigned the value currently on the display. The Convert.ToDouble()
method converts its argument to a double value. The Text property of the Label control is a string.
The actual value stored in the Text property is not a number. It’s a string such as 428,whichis
different from the numeric value 428. That’s why we use the Convert.ToDouble method to con-
vert the value of the Label’s caption to a numeric value. The remaining buttons do the same, and I
won’t show their listings here.
After the second operand is entered, the user can click the Equals button to calculate the result.
When this happens, the code of Listing 4.7 is executed.
Listing 4.7: The Equals Button
Private Sub bttnEquals Click(...) Handles bttnEquals.Click
Dim result As Double
Operand2 = Convert.ToDouble(lblDisplay.Text)
Select Case Operator
Case ”+”
result = Operand1 + Operand2
Case ”-”Petroutsos c04.tex V3 - 01/28/2008 12:36pm Page 144
144 CHAPTER 4 GUI DESIGN AND EVENT-DRIVEN PROGRAMMING
result = Operand1 - Operand2
Case ”*”
result = Operand1 * Operand2
Case ”/”
If Operand2 <> ”0” Then
result = Operand1 / Operand2
End Select
lblDisplay.Text = result.ToString
clearDisplay = True
End Sub
The result variable is declared as Double so that the result of the operation will be stored with
maximum precision. The code extracts the value displayed in the Label control and stores it in the
variable Operand2. It then performs the operation with a Select Case statement. This statement
compares the value of the Operator variable to the values listed after each Case statement. If
the value of the Operator variable matches one of the Case values, the following statement is
executed.
Division takes into consideration the value of the second operand, because if it’s zero, the
division can’t be carried out. The last statement carries out the division only if the divisor is not
zero. If Operand2 happens to be zero, nothing happens.
Now run the application and check it out. It works just like a hand-held calculator, and you
can’t crash it by specifying invalid data. We didn’t have to use any data-validation code in this
example because the user doesn’t get a chance to type invalid data. The data-entry mechanism is
foolproof. The user can enter only numeric values because there are only numeric digits on the
calculator. The only possible error is to divide by zero, and that’s handled in the Equals button.
Of course, users should be able to just type the numeric values; you shouldn’t force them
to click their digits. To intercept keystrokes from within your code, you must first set the form’s
KeyPreview property to True. Each keystroke is reported to the control that has the focus at the
time and fires the keystroke-related events: the KeyDown, KeyPress,and KeyUp events. Sometimes
we need to handle certain keystrokes from a central place, and we set the form’s KeyPreview
property to True, so that keystrokes are reported first to the form and then to the control that
has the focus. We can intercept the keystrokes in the form’s KeyPress event and handle them
in this event handler. Insert the statements shown in Listing 4.8 in the form’s KeyPress event
handler.
Listing 4.8: Handling Keystrokes at the Form’s Level
Private Sub CalculatorForm KeyPress(...) Handles Me.KeyPress
Select Case e.KeyChar
Case ”1” : bttn1.PerformClick()
Case ”2” : bttn2.PerformClick()
Case ”3” : bttn3.PerformClick()
Case ”4” : bttn4.PerformClick()
Case ”5” : bttn5.PerformClick()
Case ”6” : bttn6.PerformClick()
Case ”7” : bttn7.PerformClick()Petroutsos c04.tex V3 - 01/28/2008 12:36pm Page 145
BUILDING A CALCULATOR 145
Case ”8” : bttn8.PerformClick()
Case ”9” : bttn9.PerformClick()
Case ”0” : bttn0.PerformClick()
Case ”.” : bttnPeriod.PerformClick()
Case ”C”, ”c” : bttnClear.PerformClick()
Case ”+” : bttnPlus.PerformClick()
Case ”-” : bttnMinus.PerformClick()
Case ”*” : bttnMultiply.PerformClick()
Case ”/” : bttnDivide.PerformClick()
Case ”=” : bttnEquals.PerformClick()
End Select
End Sub
This event handler examines the key pressed by the user and invokes the Click event handler
of the appropriate button by calling its PerformClick method. This method allows you to ‘‘click’’
a button from within your code. When the user presses the digit 3,theform’s KeyPress event
handler intercepts the keystrokes and emulates the click of the bttn3 button.
Using Simple Debugging Tools
Our sample applications work nicely and are quite easy to test and fix if you discover something
wrong with them (but only because they’re very simple applications). As you write code, you’ll
soon discover that something doesn’t work as expected, and you should be able to find out why
and then fix it. The process of eliminating errors is called debugging, and Visual Studio provides
the tools to simplify the process of debugging. (These tools are discussed in detail in Appendix B.)
There are a few simple debugging techniques you should know, even as you work with simple
projects.
Open the MathCalculator project in the code editor and place the pointer in the line that calcu-
lates the difference between the two operands. Let’s pretend there’s a problem with this line, and
we want to follow the execution of the program closely to find out what’s going wrong with the
application. Press F9, and the line will be highlighted in brown. This line has become a breakpoint:
As soon as it is reached, the program will stop.
Press F5 to run the application and perform a subtraction. Enter a number; then click the minus
button, and then another number, and finally the Equals button. The application will stop, and the
code editor will open. The breakpoint will be highlighted in yellow. You’re still in runtime mode,
but the execution of the application is suspended. You can even edit the code in break mode and
then press F5 to continue the execution of the application. Hover the pointer over the Operand1
and Operand2 variables in the code editor’s window. The value of the corresponding variable will
appear in a small ToolTip box. Move the pointer over any variable in the current event handler
to see its value. These are the values of the variables just prior to the execution of the highlighted
statement.
The result variable is zero because the statement hasn’t been executed yet. If the variables
involved in this statement have their proper values (if they don’t, you know that the problem is
prior to this statement and perhaps in another event handler), you can execute this statement by
pressing F10, which executes only the highlighted statement. The program will stop at the next
line. The next statement to be executed is the End Select statement.Petroutsos c04.tex V3 - 01/28/2008 12:36pm Page 146
146 CHAPTER 4 GUI DESIGN AND EVENT-DRIVEN PROGRAMMING
Find an instance of the result variable in the current event handler, rest the pointer over it,
and you will see the value of the variable after it has been assigned a value. Now you can press
F10 to execute another statement or press F5 to return to normal execution mode.
You can also evaluate expressions involving any of the variables in the current event handler by
entering the appropriate statement in the Immediate window. The Immediate window appears at
the bottom of the IDE. If it’s not visible, open the Debug menu and chooseWindows  Immediate.
The current line in the command window is prefixed with the greater-than symbol (reminiscent of
the DOS days). Place the cursor next to it and enter the following statement:
? Operand1 / Operand2
The quotient of the two values will appear in the following line. The question mark is just a
shorthand notation for the Print command. If you want to know the current value on the calcula-
tor’s display, enter the following statement:
? lblDisplay.Text
This statement requests the value of a control’s property on the form. The current value of
the Label control’s Text property will appear in the following line. You can also evaluate math
expressions with statements such as the following:
? Math.Log(3/4)
Log() is the logarithm function and a method of the Math class. With time, you’ll discover that
the Immediate window is a handy tool for debugging applications. If you have a statement with
a complicated expression, you can request the values of the expression’s individual components
and make sure they can be evaluated.
Now move the pointer over the breakpoint and press F9 again. This will toggle the breakpoint
status, and the execution of the program won’t halt the next time this statement is executed.
If the execution of the program doesn’t stop at a breakpoint, it means that the statement is
never reached. In this case, you must search for the bug in statements that are executed before the
breakpoint is reached. If you didn’t assign the proper value to the Operator variable, the Case
clause for the subtraction operation will never be reached. You should place the breakpoint at the
first executable statement of the Equal button’s Click event handler to examine the values of all
variables the moment this subroutine starts its execution. If all variables have the expected values,
you will continue testing the code forward. If not, you’d have to test the statements that lead to
this statement— the statements in the event handlers of the various buttons.
Another simple technique for debugging applications is to print the values of certain vari-
ables in the Immediate window. Although this isn’t a debugging tool, it’s common among VB
programmers (and very practical, I might add). Many programmers print the values of selected
variables before and after the execution of some complicated statements. To do so, use the state-
ment Debug.WriteLine followed by the name of the variable you want to print, or an expression:
Debug.WriteLine(Operand1)
This statement sends its output to the Immediate window. This is a simple technique, but it
works. You can also use it to test a function or method call. If you’re not sure about the syntax of aPetroutsos c04.tex V3 - 01/28/2008 12:36pm Page 147
BUILDING A CALCULATOR 147
function, pass an expression that contains the specific function to the Debug.WriteLine statement
as an argument. If the expected value appears in the Immediate window, you can go ahead and
use it in your code.
In the project’s folder, you will find the MoreFeatures.txt document, which describes how
to add more features to the math calculator. Such features include the inversion of a number (the
1/x button), the negation of a number (the +/− button), and the usual math functions (logarithms,
square roots, trigonometric functions, and so on).
Exception Handling
Crashing this application won’t be as easy as crashing the LoanCalculator application. If you
start multiplying very large numbers, you won’t get an overflow exception. Enter a very large
number by repeatedly typing the digit 9; thenmultiply this value with another equally large value.
When the result appears, click the multiplication symbol and enter another very large value. Keep
multiplying the result with very large numbers until you exhaust the value range of the Double
data type (that is, until the result is so large that it can’t be stored to a variable of the Double
type). When this happens, the string infinity will appear in the display. This is Visual Basic’s way
of telling you that it can’t handle very large numbers. This isn’t a limitation of VB; it’s the way
computers store numeric values: They provide a limited number of bytes for each variable. (We
discussed oddities such as infinity in Chapter 2.)
You can’t create an overflow exception by dividing a number by zero, either, because the code
will not even attempt to carry out this calculation. In short, the MathCalculator application is
pretty robust. However, we can’t be sure that users won’t cause the application to generate an
exception, so we must provide some code to handle all types of errors.
Exceptions versus Errors
Errors are now called exceptions. You can think of them as exceptions to the normal (or intended)
flow of execution. If an exception occurs, the program must execute special statements to handle
the exception — statements that wouldn’t be executed normally. I think they’re called exceptions
because error is a word nobody likes, and most people can’t admit they wrote code that contains
errors. The term exception can be vague. What would you rather tell your customers: that the appli-
cation you wrote has errors or that your code has raised an exception? You may not have noticed
it, but the term bug is not used as frequently anymore; bugs are now called known issues.Theterm
debugging, however, hasn’t changed yet.
How do you prevent an exception raised by a calculation? Data validation won’t help. You
just can’t predict the result of an operation without actually performing the operation. And if the
operation causes an overflow, you can’t prevent it. The answer is to add a structured exception han-
dler. Most of the application’s code is straightforward, and you can’t easily generate an exception
for demonstration purposes. The only place where an exception may occur is the handler of the
Equals button, where the calculations take place. This is where we must add an exception handler.
The outline of the structured exception handler is the following:
Try
{ statements block}
Catch ExceptionPetroutsos c04.tex V3 - 01/28/2008 12:36pm Page 148
148 CHAPTER 4 GUI DESIGN AND EVENT-DRIVEN PROGRAMMING
{ handler block}
Finally
{ clean-up statements block}
End Try
The program will attempt to perform the calculations, which are coded in the statements block.
If the program succeeds, it continues with the cleanup statements. These statements are mostly
cleanup code, and the Finally section of the statement is optional. If missing, the program exe-
cution continues with the statement following the End Try statement. If an error occurs in the
first block of statements, the Catch Exception section is activated, and the statements in the
handler block are executed.
The Catch block is where you handle the error. There’s not much you can do about errors that
result from calculations. All you can do is display a warning and give the user a chance to change
the values. There are other types of errors, however, that can be handled much more gracefully.
If your program can’t read a file from a CD drive, you can give the user a chance to insert the CD
and retry. In other situations, you can prompt the user for a missing value and continue. If the
application attempts to write to a read-only file, for example, chances are that the user specified
a file on a CD drive, or a file with its read-only attribute set. You can display a warning, exit the
subroutine that saves the data, and give the user a chance to either select another filename or
change the read-only attribute of the selected file.
In general, there’s no unique method to handle all exceptions. You must consider all types
of exceptions that your application may cause and handle them on an individual basis. What’s
important about error handlers is that your application doesn’t crash; it simply doesn’t perform
the operation that caused the exception (this is also known as the offending operation,or offending
statement) and continues.
The error handler for the MathCalculator application must inform the user that an error
occurred and abort the calculations — not even attempt to display a result. If you open the Equals
button’s Click event handler, you will find the statements detailed in Listing 4.9.
Listing 4.9: Revised Equals Button
Private Sub bttnEquals Click(ByVal sender As System.Object,
ByVal e As System.EventArgs) Handles bttnEquals.Click
Dim result As Double
Operand2 = Convert.ToDouble(lblDisplay.Text)
Try
Select Case Operator
Case ”+”
result = Operand1 + Operand2
Case ”-”
result = Operand1 - Operand2
Case ”*”
result = Operand1 * Operand2
Case ”/”
If Operand2 <> ”0” Then result = Operand1 / Operand2
End Select
lblDisplay.Text = resultPetroutsos c04.tex V3 - 01/28/2008 12:36pm Page 149
THE BOTTOM LINE 149
Catch exc As Exception
MsgBox(exc.Message)
result = ”ERROR”
Finally
clearDisplay = True
End Try
End Sub
Most of the time, the error handler remains inactive and doesn’t interfere with the operation
of the program. If an error occurs, which most likely will be an overflow error, the error-handling
section of the Try...Catch...End Try statement will be executed. This code displays a message
box with the description of the error, and it also displays the string ERROR on the calculator’s
display. The Finally section is executed regardless of whether an exception occurred. In this
example, the Finally section sets the clearDisplay variable to True so that when another digit
button is clicked, a new number will appear on the display.
The BottomLine
Design graphical user interfaces. A Windows application consists of a graphical user inter-
face and code. The interface of the application is designed with visual tools and consists of
controls that are common to all Windows applications. You drop controls from the Toolbox
window onto the form, size and align the controls on the form, and finally set their properties
through the Properties window. The controls include quite a bit of functionality right out of the
box, and this functionality is readily available to your application without a single line of code.
Master It Describe the process of aligning controls on a form.
Program events. Windows applications follow an event-driven model:We code the events to
which we want our application to respond. The Click events of the various buttons are typical
events to which an application reacts. You select the actions to which you want your applica-
tion to react and program these events accordingly.
When an event is fired, the appropriate event handler is automatically invoked. Event han-
dlers are subroutines that pass two arguments to the application: the sender object (which is an
object that represents the control that fired the event) and the e argument (which carries addi-
tional information about the event).
Master It How will you handle certain keystrokes regardless of the control that receives
them?
Write robust applications with error handling. Numerous conditions can cause an applica-
tion to crash, but a professional application should be able to detect abnormal conditions and
handle them gracefully. To begin with, you should always validate your data before you attempt
to use them in your code. A well-known computer term is ‘‘garbage in, garbage out’’, which
means you shouldn’t perform any calculations on invalid data.
Master It How will you execute one or more statements in the context of a structured
exception handler?Petroutsos c04.tex V3 - 01/28/2008 12:36pm Page 150Petroutsos c05.tex V2 - 01/28/2008 12:46pm Page 151
Chapter 5
The Vista Interface
With the introduction of Windows Vista and the .NET Framework 3.5, Microsoft has overhauled
its entire graphical presentation technology. The new application programming interface (API)
is known as the Windows Presentation Foundation (WPF). WPF is one of the core technologies in
the .NET Framework 3.5 and is integrated into Windows Vista. WPF is also supported on
Windows XP.
WPF offers a whole new approach to developing graphical user interfaces (UIs) for the
Windows platform. WPF is designed to take advantage of the graphics engines and display
capabilities of the modern computer, and is vector based and resolution independent. This means
that your UIs automatically scale depending on the size and resolution of the user’s screen. This
is enhanced by the ability to place objects using relative positioning (so that objects sit relative to
other objects) rather than the more traditional absolute positioning used in WinForms.
You can create UIs for traditional desktop applications as well as for web-based applications
with WPF. Although initially limited to Internet Explorer for web-based applications, the
introduction of Microsoft’s Silverlight enablesWPF to go cross-browser. You can find more
information on Silverlight at www.microsoft.com/silverlight/.
WPF offers the ability to truly separate the UI from the business logic of your application.
Although we will be working with WPF in Visual Studio 2008, there are additional Microsoft
and other third-party tools for creating UIs in WPF. In particular, Microsoft’s Expression Blend
(as part of Expression Studio) offers a powerful graphical tool for creating user interface designs.
More information on Microsoft’s Expression products can be obtained from www.microsoft.com/
expression.
In this chapter, you will learn how to do the following:
◆ Create a simple WPF application
◆ Data-bind controls in WPF
◆ Use a data template to control data presentation
IntroducingXAML
Extensible Application Markup Language (XAML) is the XML language used to describe the user
interface definition. When you create a WPF-based UI, it is written into an XAML file. You can
create and edit XAML with a simple text editor such as Notepad if you wish, or use a more
sophisticated tool such as XML Notepad 2007, available from www.microsoft.com/downloads/
details.aspx?familyid = 72 d6aa49-787 d-4118-ba5f-4f30fe913628. When working
with Visual Studio 2008, you have the option to write the code behind your UIs, creatingPetroutsos c05.tex V2 - 01/28/2008 12:46pm Page 152
152 CHAPTER 5 THE VISTA INTERFACE
functionality along with your user interface; you also have the ability to create and attach any
of the full range of resources available through Visual Studio.
Documents created with XAML have the file extension .xaml. In Visual Studio 2008, the design
surface for XAML documents is a tabbed panel where you can easily swap between the graphical
user interface (GUI) view and source code view of your work.
When projects containing XAML files are compiled, the XAML is converted into Binary
Application Markup Language (BAML) before being included in the assembly. The main
purpose of this is to improve application performance because compiled BAML will process much
faster than raw XAML. From a development point of view, it is not necessary to have an intimate
understanding of BAML because you will be working mainly in XAML. It is, however, possible
to manually compile your XAML into BAML by using the Windows Application Compiler that
ships with the .NET Framework, if you are so inclined.
XAML is very flexible, and can be used to represent everything from layout panels, controls,
and graphics to 3D representations and animations.
The following code snippet gives the basic format of an XAML page. This is the typical code
skeleton that is generated when you create a newWPF window in Visual Studio 2008. Most of the
formatting and layout code will go between the <Grid> tags.
<Window x:Class=”Window3”
xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”
xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”
Title=”Window3” Height=”300” Width=”300”>
<Grid>
</Grid>
The following XAML snippet demonstrates the representation for a TextBox control. Normally,
if we were to drop a TextBox control onto the XAML page shown in the preceding code, the
following code would appear between the <Grid> tags:
<TextBox Height=”21” Margin=”56,106,102,0” Name=”TextBox1”
VerticalAlignment=”Top” BorderThickness=”2”>
<TextBox.BitmapEffect>
<EmbossBitmapEffect />
</TextBox.BitmapEffect>
</TextBox>
This example of the TextBox control not only includes some basic properties for the
control such as Name and Height, but also includes a more sophisticated visual effect property:
EmbossBitmapEffect.
You can also use XAML to generate simple shapes (primitives) such as circles and rectangles.
The following code snippet demonstrates how you can use XAML to generate a simple rectangle:
<Rectangle Height=”74” Margin=”20,0,64,27” Name=”Rectangle1”
Stroke=”Black” VerticalAlignment=”Bottom” Fill=”Red”>
<Rectangle.BitmapEffect>
<DropShadowBitmapEffect />
</Rectangle.BitmapEffect>
</Rectangle>Petroutsos c05.tex V2 - 01/28/2008 12:46pm Page 153
INTRODUCING THE WPF CONTROLS 153
In this example, the rectangle has a red fill color and black border. We have also included a
drop shadow effect. These effects were all achieved by simply modifying the Properties window
for the TextBox and Rectangle controls. However, many of WPF’s properties are available to be
used across virtually all the controls, even if they do not appear in the Properties window. For
example, you can rewrite the code snippet for the rectangle to modify the Fill property to create
a linear gradient from red to blue, as follows:
<Rectangle Height=”74” Margin=”20,0,64,27” Name=”Rectangle1”
Stroke=”Black” VerticalAlignment=”Bottom” >
<Rectangle.BitmapEffect>
<DropShadowBitmapEffect />
</Rectangle.BitmapEffect>
<Rectangle.Fill>
<LinearGradientBrush>
<GradientStop Color=”Red” Offset=”0”/>
<GradientStop Color=”Blue” Offset=”1”/>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
You cannot achieve this effect through the available settings in the Properties window for the
Rectangle — it needs to be typed directly into the XAML.
A number of these additional properties can be picked up from using Microsoft’s Expression
Blend tool to create XAML pages and examining the code generated. Refer to the ‘‘Expression Blend
Overview’’ section later in this chapter.
Introducing theWPF Controls
When you first open aWPF project in Visual Studio 2008, you will notice that the Toolbox contains
a very similar set of controls to those found in the other development environments. However,
although most of the controls appear similar, they can behave very differently.
This section introduces some of the core controls and lists some of the fundamental differences
between them and their traditional Windows counterparts.
One of the first things you will notice when opening up Visual Studio 2008 to a WPF
page is that the page has a magnification control in the top-left corner, and resizing the page
automatically resizes any controls on the page as well. Controls stay placed on the page relative
to each other, rather than fixed into some position relative to the page itself. Controls also behave
differently from the traditional Windows Forms model as you move them around the page, and
the underlying form (or rather window) itself looks a little different.
To create a newWPF project, choose File  New Project. In the New Project dialog box, choose
WPF Application. Keep the default name and click OK. In keeping with the idea that you are
designing an interface separated from the business logic of your application, the default designer
opens to Window1.xaml.
If you examine the Toolbox on the left side of Visual Studio, you will see many familiar items
from the Standard Toolbox there. However, as you will see, some of them exhibit different
behavior. Note that all the WPF controls differ from their WinForms cousins in the wide rangePetroutsos c05.tex V2 - 01/28/2008 12:46pm Page 154
154 CHAPTER 5 THE VISTA INTERFACE
of style and formatting properties that are available to them. Table 5.1 gives a brief overview of
the roles of some of the new controls and some changes in the existing controls. This is not an
exhaustive list of the controls, but it does look at some key ones.
Table 5.1: The WPF Standard Controls
WPF Control Features
Border Creates a border around an object. A broad range of properties exists to enable you to
change the appearance of the border.
Button Apart from the obvious and extensive range of style properties common to virtually all
the WPF controls, the Button control looks and behaves as you would expect.
Double-clicking the control opens to a code skeleton for the Button Click event
handler in code-behind (typically Window1.xaml.vb).
Grid Defines a tabular area of columns and rows. Useful for displaying data. One of the main
layout controls.
Label Displays data via the Content property.
StackPanel Arranges child components (other controls) either horizontally or vertically. One of the
main layout controls.
TextBox Unlike the Label control, TextBox continues to use the Text property to display data.
Canvas Provides an area where you can explicitly position controls relative to the position of
the Canvas control.
DockPanel Provides a space where you arrange controls vertically or horizontally relative to each
other. One of the main layout controls.
InkCanvas Sets up a drawing surface within your application.
MediaElement Contains audio or video content.
TextBlock Displays small chunks of text.
UniformGrid Creates a grid with fixed cell size.
WrapPanel Displays elements sequentially (depending on the orientation property) and wraps to
the next line where necessary.
Next we will create a couple of simple applications to demonstrate the functionality of the
WPF controls.
Simple ‘‘Hello World’’ WPF Application
In this section, you will come to grips with setting up a basic WPF application and using the
controls by creating a simple ‘‘Hello World’’ application.Petroutsos c05.tex V2 - 01/28/2008 12:46pm Page 155
INTRODUCING THE WPF CONTROLS 155
Open Visual Studio 2008 and complete the following steps:
1. Choose File  New Project. From the New Project dialog box, chooseWPF Application and
rename the project MyWpfApplication1. Click OK.
2. The Designer should open to Window1.xaml in Split mode (Design And Source Code). If
you need more space, you can collapse the bottom pane (usually the XAML pane, although
this can be swapped around) and tab between the two views.
3. Drag a Button control and a Label control onto your form.Move the controls around on the
form to see how the positioning and resize properties work. The <Grid> tags in XAML
view should now contain the following (without the line breaks and with variations in the
Height and Margin properties):
<Grid>
<Button Height=”23” HorizontalAlignment=”Left” Margin=”43,19,0,0”
Name=”Button1” VerticalAlignment=”Top” Width=”75”>Button</Button>
<Label Height=”23” Margin=”49,83,109,0” Name=”Label1”
VerticalAlignment=”Top”>Label</Label>
</Grid>
4. In Design mode, double-click the Button control to enter code-behind. This should open
up to Window1.xaml.vb with a Button1 Click event code skeleton. Compete the
Button1 Click event with the following code:
Private Sub Button1 Click(ByVal sender As System.Object, ByVal e As
System.Windows.RoutedEventArgs) Handles Button1.Click
Label1.Content = ”Hi There!”
End Sub
Press F5 to run the application. Figure 5.1 illustrates the running application in the Designer
window after Button1 has been clicked.
Figure 5.1
The running
MyWpfApplicationPetroutsos c05.tex V2 - 01/28/2008 12:46pm Page 156
156 CHAPTER 5 THE VISTA INTERFACE
Simple Drawing Program
This next example illustrates how you can easily create a simple drawing program in WPF with a
few controls and very little code. In this example, we will use the InkCanvas control to create the
drawing surface. The control will be added at runtime, enabling us to dynamically alter properties
such as pen color. We will use a ComboBox control to set the pen color and a Button control to
clear the screen.
Begin by opening Visual Studio 2008 and choosing File  New Project. Then complete the
following steps:
1. From the New Project dialog box, chooseWPF Application and rename the project
WpfDraw. Click OK.
2. This should open Window.xaml in Split mode. From the Standard Toolbox, drop a
StackPanel control onto the Window1 form on the Design surface and set the Margin
property of the StackPanel (in the Properties box) to ‘‘0,0,0,25’’. This Margin property will
extend the StackPanel to the top, left, and right borders of the form. The margin will leave
a 25-pixel (px) gutter at the bottom of the form, where we can place a Button control. Keep
the default name for the control of StackPanel1.
3. Set the VerticalAlignment property of StackPanel1 to Top.
4. From the Standard Toolbox, drop a ComboBox control into the StackPanel1 control. In the
Properties window for the ComboBox control, set the HorizontalAlignment property to
Left. Keep the default name of ComboBox1. The ComboBox should be located in the top-left
corner of Window1.
5. In the XAML window, adjust the entry for ComboBox1 to read as shown here. Keep any
of the illustrated line breaks on a single line. This will create the pen color items in the
ComboBox.
<ComboBox Height=”25” Name=”ComboBox1” Width=”120”
HorizontalAlignment=”Left”>
<ComboBoxItem IsSelected=”True”>Red Pen</ComboBoxItem>
<ComboBoxItem>Green Pen</ComboBoxItem>
<ComboBoxItem>Blue Pen</ComboBoxItem>
</ComboBox>
6. From the Standard Toolbox, drop a Button control onto Window1 in the space under
the StackPanel. Keep the default name of Button1. In the Properties window for Button1,
set the Height property to 25, the VerticalAlignment property to Bottom, the
HorizontalAlignment to Left, and the Content property to Clear Screen.
7. The final XAML markup for Button1 should be similar to the following snippet (ignore the
line break):
<Button Height=”25” Name=”Button1” Width=”75” HorizontalAlignment=”Left”
VerticalAlignment=”Bottom”>Clear Screen</Button>Petroutsos c05.tex V2 - 01/28/2008 12:46pm Page 157
INTRODUCING THE WPF CONTROLS 157
Listing 5.1 gives the full XAML markup for this stage of the development.
Listing 5.1: Full XAMLMarkup forWpfDraw
<Window x:Class=”Window1”
xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”
xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”
Title=”Window1” Height=”300” Width=”300”>
<Grid>
<StackPanel Name=”StackPanel1” Margin=”0,0,0,25”
VerticalAlignment=”Top”>
<ComboBox Height=”25” Name=”ComboBox1” Width=”120”
HorizontalAlignment=”Left”>
<ComboBoxItem IsSelected=”True”>Red Pen</ComboBoxItem>
<ComboBoxItem>Green Pen</ComboBoxItem>
<ComboBoxItem>Blue Pen</ComboBoxItem>
</ComboBox>
</StackPanel>
<Button Height=”25” Name=”Button1” Width=”75”
HorizontalAlignment=”Left” VerticalAlignment=”Bottom”>
Clear Screen</Button>
</Grid>
</Window>
The next step is to add the code-behind for the application. In the Design window,
double-click the Button1 control to enter code-behind (Window1.xaml.vb). Continue with the
following steps:
1. Directly under the Class Window declaration, add the following line of code to declare an
instance of the InkCanvas control:
Dim myink As New InkCanvas
2. Select the Window1 Loaded code skeleton (choose Class Name  Window Events, and
Method Name  Loaded).
3. Add the following line of code to the Window Loaded skeleton. This will add the InkCanvas
control to the StackPanel control in Window1 when the window loads:
StackPanel1.Children.Add(myink)
4. In the Button1 Click event handler, add the following line of code. This will enable the
user to erase any drawings he has created:
myink.Strokes.Clear()Petroutsos c05.tex V2 - 01/28/2008 12:46pm Page 158
158 CHAPTER 5 THE VISTA INTERFACE
5. Selectthecodeskeletonfor ComboBox1 SelectionChanged and add the following snippet.
This code will set the pen color of the InkCanvas control depending on the selected color in
the ComboBox:
If ComboBox1.SelectedIndex = 0 Then
myink.DefaultDrawingAttributes.Color = Colors.Red
ElseIf ComboBox1.SelectedIndex = 1 Then
myink.DefaultDrawingAttributes.Color = Colors.Green
ElseIf ComboBox1.SelectedIndex = 2 Then
myink.DefaultDrawingAttributes.Color = Colors.Blue
End If
Listing 5.2 gives the full code-behind listing forWpfDraw.
Listing 5.2: Full Code-Behind Listing forWpfDraw
Class Window1
Dim myink As New InkCanvas
Private Sub Window1 Loaded(ByVal sender As Object, ByVal e As
System.Windows.RoutedEventArgs) Handles Me.Loaded
StackPanel1.Children.Add(myink)
End Sub
Private Sub Button1 Click(ByVal sender As System.Object,
ByVal e As System.Windows.RoutedEventArgs) Handles Button1.Click
myink.Strokes.Clear()
End Sub
Private Sub ComboBox1 SelectionChanged(ByVal sender As
System.Object, ByVal e As
System.Windows.Controls.SelectionChangedEventArgs) Handles
ComboBox1.SelectionChanged
If ComboBox1.SelectedIndex = 0 Then
myink.DefaultDrawingAttributes.Color = Colors.Red
ElseIf ComboBox1.SelectedIndex = 1 Then
myink.DefaultDrawingAttributes.Color = Colors.Green
ElseIf ComboBox1.SelectedIndex = 2 Then
myink.DefaultDrawingAttributes.Color = Colors.Blue
End If
End Sub
End ClassPetroutsos c05.tex V2 - 01/28/2008 12:46pm Page 159
DATA-BINDING WPF CONTROLS 159
Press F5 to test the application. Figure 5.2 illustrates the running application.
Figure 5.2
The running WpfDraw
Data-BindingWPF Controls
The ability to bind WPF controls to external (and internal) data sources is an important aspect of
being able to separate UI design from business logic and functionality.
WPF controls can be bound to data sources as well as style sources. This way, you can set
up a set of styles for your application and then bind each control to those styles, thus enabling
centralized management of the look and feel of your user interfaces.
There are many options available to the developer when connecting to data with WPF. WPF
offers a flexible and powerful framework for data connectivity and management. A full discussion
is beyond the scope of this chapter; refer to the Microsoft documentation for a more detailed view.
The article titled ‘‘Data Binding Overview’’ (search the Help documentation) is a good start.
In this section, you will see how to carry out three basic data-binding tasks withWPF. You will
bind controls to an array, to a data template, and to a database.
Data-Binding Example 1: Binding to an Array and a Data Template
In this example, we will set up a simple class called Contacts to hold Surname and FirstName
elements. We will then use an array of contacts to hold some data that can be accessed from our
XAML page. The presentation of the data in the XAML page will be defined by a data template.
To keep things simple, we will do everything within the one project.
Begin by opening Visual Studio 2008 and choosing File  New Project. Then complete the
following steps:
1. From the New Project dialog box, chooseWPF Application and rename the project
WpfBinding1. Click OK.
2. We will begin by creating the Contacts class. You should be open to Window1.xaml in
Split view. From Solution Explorer, click the View Code icon to switch to code-behindPetroutsos c05.tex V2 - 01/28/2008 12:46pm Page 160
160 CHAPTER 5 THE VISTA INTERFACE
(Window1.xaml.vb). Alternatively, you can double-click on the Window1 heading in the
Window1 form in the Designer.
3. In code-behind, add the following code for the Contacts class directly under the Class
Window1 declaration. The Contacts class has two properties: FirstName and Surname.You
will find that as you type the following code, much of the code skeleton is automatically
generated by Visual Studio:
Private Class contacts
Dim name As String
Dim surname As String
Public Sub New(ByVal FirstName As String, ByVal Surname As String)
name = FirstName
surname = Surname
End Sub
Public ReadOnly Property FirstName() As String
Get
Return name
End Get
End Property
Public ReadOnly Property Surname() As String
Get
Return surname
End Get
End Property
End Class
4. Select the Window1 Loaded code skeleton (choose Class Name  Window Events, and
Method Name  Loaded).
5. Add the following code to the Window1 Loaded skeleton. This code declares an array of
contacts named person and adds some names to it. The final line uses the DataContext
property of Window1 to bind the person array to Window1.
Dim person As New ArrayList
person.Add(New contacts(”Fred”, ”Bloggs”))
person.Add(New contacts(”Betty”, ”Smith”))
person.Add(New contacts(”Jane”, ”Doe”))
person.Add(New contacts(”Bill”, ”Jones”))
person.Add(New contacts(”Jenny”, ”Day”))
Me.DataContext = person
This completes the code-behind for this project.
The next set of steps involves setting up the XAML for the project. For this example, we will
write directly to the XAML code. You will find that as you continue to work with WPF, it is often
easier to work directly with the XAML and use the Designer only to provide a visual check that
everything is going together as it should.Petroutsos c05.tex V2 - 01/28/2008 12:46pm Page 161
DATA-BINDING WPF CONTROLS 161
1. Switch back to XAML view for Window1.xaml. The default XAML should look similar to
the following snippet:
<Window x:Class=”Window4”
xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”
xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”
Title=”Window4” Height=”300” Width=”300” Name=”Window1”>
<Grid>
</Grid>
</Window>
2. Our key areas of interest here are the <Grid> tags. Add a name attribute to the <Grid>
tag to read <Grid Name = ‘‘MyGrid’’>.
3. The next step is to add the data template. This is contained within <GridResources> tags,
which sit within the <Grid Name = ‘‘MyGrid’’> tags. Create the following snippet:
<Grid.Resources>
<DataTemplate x:Key=”NameStyle”>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width=”60” />
<ColumnDefinition Width=”*” />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column=”0” Text=”{Binding Path=FirstName}”/>
<TextBlock Grid.Column=”1” Text=”{Binding Path=Surname}”/>
</Grid>
</DataTemplate>
</Grid.Resources>
The purpose of this code is to define a data template that can be referenced by using its key,
NameStyle. The data template defines a grid with two columns — the first column is 60px
wide, and the second column occupies the remainder of the available space. Attached to
the first column is a TextBlock control that has its Text property bound to the FirstName
element of the person array that we declared in the code-behind. Remember that we used
the DataContext property to attach person to Window1. Similarly, the second TextBlock
has its Text property bound to Surname, and the control is attached to the second column
of the grid.
4. Add the following snippet directly below the <Grid.Resources >...< /Grid.Resources>
section. Do not include the line breaks:
<TextBlock Text=”Current Selection = ” />
<TextBlock Text=”{Binding Path=FirstName}” Margin=”110,0,0,0” />
<ListBox Margin=”0,40,0,0”
ItemTemplate=”{StaticResource NameStyle}” ItemsSource=”{Binding} ”
IsSynchronizedWithCurrentItem=”true” Name=”ListBox1” />Petroutsos c05.tex V2 - 01/28/2008 12:46pm Page 162
162 CHAPTER 5 THE VISTA INTERFACE
The purpose of this code is to provide the content controls (two TextBlocks and a ListBox)
to present the data on the window. We have used the Margin property to control the layout of
the controls. In this instance, the use of Margin is suitable because we wish to simplify the code,
but laying out in this manner is limiting if you wish to alter text content or styles at a later date.
There are a number of alternative methods for achieving the same layout using combinations of
the layout controls, which provide a little more flexibility. For example, the following snippet
achieves the same result by using Grid rows and a nested StackPanel control to manage the layout
of the content controls:
<Grid.RowDefinitions>
<RowDefinition Height=”40” />
<RowDefinition Height=”*” />
</Grid.RowDefinitions>
<StackPanel Orientation=”Horizontal” Grid.Row=”0”>
<TextBlock Text=”Current Selection = ” />
<TextBlock Text=”{Binding Path=FirstName}”/>
</StackPanel>
<ListBox Grid.Row=”1”
ItemTemplate=”{StaticResource NameStyle}” ItemsSource=”{Binding} ”
IsSynchronizedWithCurrentItem=”true” Name=”ListBox1” />
The second TextBlock uses Binding to data-bind the control to the FirstName field of the
person array. The ListBox uses the StaticResource statement to bind to the NameStyle data
layout. By simply writing Binding, we indicate that the control is bound to the data source (in this
case, the person array) attached to the parent container (Window1). In this way, the ListBox will
display the full set of records in the array. The NameStyle data layout ensures that each record
is displayed as a FirstName, Surname combination by using two TextBlock controls set up in
adjacent Grid columns.
Listing 5.3 gives the full XAML source for this project. Delete the line breaks.
Listing 5.3: Full XAML Source Code for theWpfBinding1 Project
<Window x:Class=”Window1”
xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”
xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”
Title=”Window1” Height=”300” Width=”300”>
<Grid Name=”myGrid”>
<Grid.Resources>
<DataTemplate x:Key=”NameStyle”>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width=”60” />
<ColumnDefinition Width=”*” />
</Grid.ColumnDefinitions>Petroutsos c05.tex V2 - 01/28/2008 12:46pm Page 163
DATA-BINDING WPF CONTROLS 163
<TextBlock Grid.Column=”0” Text=”{Binding
Path=FirstName}”/>
<TextBlock Grid.Column=”1”
Text=”{Binding Path=Surname}”/>
</Grid>
</DataTemplate>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition Height=”40” />
<RowDefinition Height=”*” />
</Grid.RowDefinitions>
<StackPanel Orientation=”Horizontal” Grid.Row=”0”>
<TextBlock Text=”Current Selection = ” />
<TextBlock Text=”{Binding Path=FirstName}”/>
</StackPanel>
<ListBox Grid.Row=”1”
ItemTemplate=”{StaticResource NameStyle}”
ItemsSource=”{Binding} ” IsSynchronizedWithCurrentItem=”true”
Name=”ListBox1” />
</Grid>
</Window>
Finish up by testing the application. Making a selection in the list should be reflected in the
Current Selection TextBlock. The running application is shown in Figure 5.3.
Figure 5.3
The running
WpfBinding1 projectPetroutsos c05.tex V2 - 01/28/2008 12:46pm Page 164
164 CHAPTER 5 THE VISTA INTERFACE
Data-Binding Example 2: Binding to a Database
In this next example, we will see how to connect the WPF page created in the previous example
to a database. This project not only demonstrates the technique of connecting to a database, but
also emphasizes the way that WPF enables the developer to separate the user interface aspects of
a project from the data and business logic.
Begin by opening the previous project,WpfBinding1. Continue with the following steps:
1. First, we will use SQL Server Express to create a new database. From the Project menu,
choose Add New Item.
2. From the Add New Item dialog box, select Service-Based Database. Name the database
Contacts.mdf and click the Add button.
3. You will be presented with the Data Source Configuration Wizard with a message saying
that the database does not contain any objects. Keep the default name: ContactsDataSet.
Click the Finish button to create an empty dataset and close the wizard. We will return to
the dataset later to complete it.
4. Over in the Toolbox area, click the Server Explorer tab. Under the Data Connections tree,
expand the entry for Contacts.mdf. Right-click the Tables entry and choose Add New
Table from the context menu.
5. The new database table should now be open in the Designer window. Set up the database
fieldsasshowninTable5.2.
Table 5.2: Database Fields for Contacts.mdf
Column Name Data Type
ID nchar(10)
FirstName nchar(10)
Surname nchar(10)
6. Right-click the ID entry and choose Set Primary Key from the context menu.
7. Click the Save button on the Standard Toolbar. A Choose Name dialog box should
open for the table. Enter Customers and click OK. The Customers table should now be
visible in the Tables entry of Contacts.mdf in the Data Connections tree in
Server Explorer.
8. Over in Solution Explorer, double-click the entry for ContactsDataSet.xsd to open the
dataset in the Designer window.
9. Return to the Server Explorer. Drag the Customers table from the Data Connections tree in
Server Explorer onto the Design surface for the dataset. This should set up the Customers
DataTable in the dataset and also establish a CustomersTableAdapter.Petroutsos c05.tex V2 - 01/28/2008 12:46pm Page 165
DATA-BINDING WPF CONTROLS 165
10. Save your work.
11. In Server Explorer, right-click the Customers table in the Data Connections tree and choose
Show Table Data.
12. Enter the data from Table 5.3 into the Customers table.
Table 5.3: Data Entries for Customers Table
ID FirstName Surname
1 Fred Bloggs
2 Wilma Smith
3 Bill Green
13. You are now ready to attach the database to Window1.xaml. We will do this in
code-behind for Window1.xaml. Double-click the entry for Window1.xaml.vb in
Solution Explorer.
14. Add the following snippet to the code screen just under the entry for Class Window1.
This code declares instances of the ContactsDataSet and the CustomersTableAdapter.
Dim myDataset As New ContactsDataSet
Dim myCustomersAdapter As New
ContactsDataSetTableAdapters.CustomersTableAdapter
15. In the sub for Window1 Loaded, add the following code snippet. The purpose of the
first of these lines is to use the myCustomersAdapter to load the data from the Contacts
database into myDataset. The second line attaches the dataset as the data source
for Window1:
myCustomersAdapter.Fill(myDataset.Tables(”Customers”))
Me.DataContext = myDataset.Tables(”Customers”)
16. Finally, comment out (or delete) the line of code attaching the original person array to
Window1: ‘Me.DataContext = person.
The field names in the dataset are the same as the ones used in the original array, so we
can run the application without needing to change Window1.xaml. Note that you can now
delete or comment out any of the code that we originally used to create the person array or
contacts class.
This project is now completed. Test the application by pressing F5 or clicking the green arrow.
The running version should appear as shown in Figure 5.4.Petroutsos c05.tex V2 - 01/28/2008 12:46pm Page 166
166 CHAPTER 5 THE VISTA INTERFACE
Figure 5.4
The updated
WpfBinding1 application
Creating aWPF BrowserApplication
WPF applications can also be displayed as web applications. The XAML for such an application is
virtually identical to its desktop equivalent. You may have to make only some minor layout and
presentation changes to optimize the appearance of your application in a web browser window as
opposed to on the desktop.
The main limitation to this technology is that such applications are restricted to Internet
Explorer 6 and above unless you create a Silverlight implementation. Microsoft is currently
releasing Silverlight as a cross-platform, cross-browser plug-in. Refer to www.microsoft.com
/silverlight for more information.
In this example, we will create a WPF browser version of the simple drawing program,
WpfDraw, that we created earlier in this chapter.
1. Start Visual Studio 2008 and begin by choosing File  New Project.
2. In the New Project dialog box, chooseWPF Browser Application. Keep the default name of
WpfBrowserApplication1 and click OK.
3. This will open up Page1.xaml in Split mode. Switch to XAML view and add the code from
Listing 5.4 (without the line breaks). This is essentially the same markup that we used for
WpfDraw earlier in the chapter, with some layout modifications to control the distribution
of the controls and make the application a little more presentable in a web browser
window.
Listing 5.4: XAML for WpfBrowserApplication1
<Page x:Class=”Page1”
xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”
xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”
Title=”Page1”>
<Grid>
<StackPanel Margin=”25”>
<StackPanel Name=”StackPanel1” Margin=”0,0,0,0”Petroutsos c05.tex V2 - 01/28/2008 12:46pm Page 167
CREATING A WPF BROWSER APPLICATION 167
VerticalAlignment=”Top”>
<ComboBox Height=”25” Name=”ComboBox1” Width=”120”
HorizontalAlignment=”Left”>
<ComboBoxItem IsSelected=”True”>Red Pen
</ComboBoxItem>
<ComboBoxItem>Green Pen</ComboBoxItem>
<ComboBoxItem>Blue Pen</ComboBoxItem>
</ComboBox>
</StackPanel>
<Button Height=”25” Name=”Button1” Width=”75”
HorizontalAlignment=”Left” VerticalAlignment=”Bottom”>Clear Screen
</Button>
</StackPanel>
</Grid>
</Page>
4. The code-behind forWpfBrowserApplication1 remains virtually unchanged from
WpfDraw as well. Use the Solution Explorer to switch to code-behind (Page1.xaml.vb)
and add the code from Listing 5.5. The only difference with this code from the original
WpfDraw is that we add some additional layout and design properties to myink in the
Page1 Loaded sub to again help with presentation in the web browser window.
Listing 5.5: Code-Behind for WpfBrowserApplication1
Class Page1
Dim myink As New InkCanvas
Private Sub Page1 Loaded(ByVal sender As Object,
ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded
StackPanel1.Children.Add(myink)
myink.Background = Brushes.Cornsilk
myink.HorizontalAlignment = Windows.HorizontalAlignment.Left
myink.Height = 400
myink.Width = 400
End Sub
Private Sub Button1 Click(ByVal sender As System.Object,
ByVal e As System.Windows.RoutedEventArgs) Handles Button1.Click
myink.Strokes.Clear()
End Sub
Private Sub ComboBox1 SelectionChanged(ByVal sender As
System.Object, ByVal e As
System.Windows.Controls.SelectionChangedEventArgs) Handles
ComboBox1.SelectionChanged
If ComboBox1.SelectedIndex = 0 Then
myink.DefaultDrawingAttributes.Color = Colors.Red
ElseIf ComboBox1.SelectedIndex = 1 Then
myink.DefaultDrawingAttributes.Color = Colors.GreenPetroutsos c05.tex V2 - 01/28/2008 12:46pm Page 168
168 CHAPTER 5 THE VISTA INTERFACE
ElseIf ComboBox1.SelectedIndex = 2 Then
myink.DefaultDrawingAttributes.Color = Colors.Blue
End If
End Sub
End Class
Press F5 or click the green arrow to run the application. It will open in a web browser window
and should appear and function as shown in Figure 5.5.
Figure 5.5
The running
WpfBrowserApplication1
Expression BlendOverview
Expression Blend is part of Microsoft’s new Expression Studio package. Expression Studio con-
tains four major components:
Expression Web Website designer
Expression Blend User interface designer
Expression Design Vector and bitmap graphics editor
Expression Media Media manager
Each component can be purchased independently or as part of the Expression Studio
package. A fifth component, Expression Encoder, is currently available as an independent
purchase. Expression Encoder encodes media content into a format suitable for Silverlight.Petroutsos c05.tex V2 - 01/28/2008 12:46pm Page 169
EXPRESSION BLEND OVERVIEW 169
More information and trial downloads can be obtained from the Microsoft website at
www.microsoft.com/expression/expression-studio/overview.aspx.
Expression Blend is specifically designed as a tool for creating XAML-based (WPF) interfaces. It
has amuch stronger graphical focus on design than theWPF tools in Visual Studio 2008 andwould
suit developers who are more focused on visual design than working with code. Expression Blend
also exposes a greater range of tools and properties at the GUI level than Visual Studio 2008,
and as such is a great way to explore the possibilities of UI design with WPF. For example, if
you are interested in animation with WPF, and want to work with Visual Studio 2008 but are
struggling with the documentation, you can create a simple animation in Expression Blend using
the graphical tools, examine the XAML markup that is generated, and transfer it to Visual Studio.
A demonstration version of Expression Blend can be downloaded from the Microsoft website.
A number of samples ship with the package that you can open and work with.
Figure 5.6 illustrates the interface for Expression Blend with a simple animation project.
Figure 5.7 illustrates the simple animation project shown in Figure 5.6, running in a test window.
Figure 5.6
Interface for Expression
Blend
Figure 5.7
Expression Blend with
running projectPetroutsos c05.tex V2 - 01/28/2008 12:46pm Page 170
170 CHAPTER 5 THE VISTA INTERFACE
We can extract the markup for the animated rectangle from the XAML generated in Expression
Blend and use it in a Visual Studio 2008 WPF project. Two sections of code are used to generate
the animation. The first section is the code defining the rectangle and its behavior, as illustrated
in the following code snippet. This code includes the red-to-blue fill gradient that was illustrated
earlier in the chapter (remove the line breaks):
<Rectangle HorizontalAlignment=”Left” Margin=”57,106,0,0”
VerticalAlignment=”Top” Width=”165” Height=”111”
Stroke=”#FF000000” RenderTransformOrigin=”0.5,0.5”
x:Name=”rectangle”>
<Rectangle.Fill>
<LinearGradientBrush EndPoint=”1,0.5” StartPoint=”0,0.5”>
<GradientStop Color=”Red” Offset=”0”/>
<GradientStop Color=”Blue” Offset=”1”/>
</LinearGradientBrush>
</Rectangle.Fill>
<Rectangle.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX=”1” ScaleY=”1”/>
<SkewTransform AngleX=”0” AngleY=”0”/>
<RotateTransform Angle=”0”/>
<TranslateTransform X=”0” Y=”0”/>
</TransformGroup>
</Rectangle.RenderTransform>
</Rectangle>
The second code chunk used with the animation is the one generated to handle the actual time
line of the animation and the trigger used to start it. In this example, the code has been doctored
slightly to animate only a single rectangle and to be used in a Page.xaml file in a WPF browser
application. The trigger to fire the animation in this case is the loading of the page. The time line
information is managed in the <Page.Resources> tags while the triggers are handled by the
<Page.Triggers> tags. The code is illustrated in the following snippet (remove the line breaks):
<Page.Resources>
<Storyboard x:Key=”Timeline1”>
<DoubleAnimationUsingKeyFrames BeginTime=”00:00:00”
Storyboard.TargetName=”rectangle”
Storyboard.TargetProperty=”(UIElement.RenderTransform).
(TransformGroup.Children)[2].(RotateTransform.Angle)”>
<SplineDoubleKeyFrame KeyTime=”00:00:05” Value=”500”/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</Page.Resources>
<Page.Triggers>
<EventTrigger RoutedEvent=”FrameworkElement.Loaded”>
<BeginStoryboard Storyboard=”{StaticResource Timeline1}”/>
</EventTrigger>
</Page.Triggers>Petroutsos c05.tex V2 - 01/28/2008 12:46pm Page 171
THE BOTTOM LINE 171
To test this code in aWPF browser application, create a newWPF Browser Application project.
Copy the contents of the <Page.Resources> and <Page.Triggers > code from the preceding
snippet into the XAML for Page1.xaml, immediately following the class declaration and before
the <Grid> tags. Copy the first code snippet governing the rectangle and its behavior between the
<Grid>. . .</Grid> tags. Run the project and you should have a rectangle performing a rotation
in a web page. If you play with the attribute values in the <SplineDoubleKeyFrame> tag, you can
alter the length and extent of the rotation.
A comprehensive user guide is available under the Help menu of the Expression Blend
package.
The BottomLine
Create a simple WPF application. WPF is a new and powerful technology for creating user
interfaces. WPF is one of the core technologies in the .NET Framework 3.5 and is integrated
into Windows Vista. WPF is also supported on Windows XP. WPF takes advantage of the
graphics engines and display capabilities of the modern computer and is vector based and res-
olution independent.
Master It Develop a simple ‘‘Hello World’’ type of WPF application that displays a
Button control and Label control. Clicking the button should set the content property of a
Label control to Hi There!
Data-bind controls inWPF. The ability to bind controls to a data source is an essential aspect
of separating the UI from the business logic in an application.
Master It Data-bind a Label control to one field in a record returned from a database on
your computer.
Use a data template to control data presentation. WPF enables a very flexible approach to
presenting data by using data templates. The developer can create and fully customize data
templates for data formatting.
Master It Create a data template to display a Name, Surname, Gender combination in
a horizontal row in a ComboBox control. Create a simple array and class of data to feed the
application.Petroutsos c05.tex V2 - 01/28/2008 12:46pm Page 172Petroutsos c06.tex V3 - 01/28/2008 12:50pm Page 173
Chapter 6
Basic Windows Controls
In previous chapters, we explored the environment of Visual Basic and the principles of
event-driven programming, which is the core of VB’s programming model. In the process,
we briefly explored a few basic controls through the examples. The .NET Framework provides
many more controls, and all of them have a multitude of trivial properties (such as Font,
BackgroundColor, and so on), which you can set either in the Properties window or from within
your code.
This chapter explores in depth the basic Windows controls: the controls you’ll use most often
in your applications because they are the basic building blocks of typical rich client-user
interfaces. Rather than look at controls’ background and foreground color, font, and other trivial
properties, we’ll look at the properties unique to each control and see how these properties are
used in building functional, rich user interfaces.
In this chapter, you’ll learn how to do the following:
◆ Use the TextBox control as a data-entry and text-editing tool
◆ Use the ListBox, CheckedListBox, and ComboBox controls to present lists
of items
◆ Use the ScrollBar and TrackBar controls to enable users to specify sizes and positions with
the mouse
The TextBox Control
The TextBox control is the primary mechanism for displaying and entering text. It is a small text
editor that provides all the basic text-editing facilities: inserting and selecting text, scrolling if the
text doesn’t fit in the control’s area, and even exchanging text with other applications through
the Clipboard.
The TextBox control is an extremely versatile data-entry tool that can be used for entering
and editing single lines of text, such as a number or a password, or an entire text file. Figure 6.1
shows a few typical examples. All the boxes in Figure 6.1 contain text — some a single line, some
several lines. The scroll bars you see in some text boxes are part of the control. You can specify
which scroll bars (vertical and/or horizontal) will be attached to the control, and they will appear
automatically whenever the control’s contents exceed the visible area of the control.Petroutsos c06.tex V3 - 01/28/2008 12:50pm Page 174
174 CHAPTER 6 BASIC WINDOWS CONTROLS
Figure 6.1
Typical uses of the
TextBox control
Basic Properties
Let’s start with the properties that specify the appearance and, to some degree, the functionality of
the TextBox control; these properties are usually set at design time through the Propertieswindow.
Then, we’ll look at the properties that allow you to manipulate the control’s contents and interact
with users from within your code.
TextAlign
This property sets (or returns) the alignment of the text on the control, and its value is a member
of the HorizontalAlignment enumeration: Left, Right,or Center. The TextBox control doesn’t
allow you to format text (mix different fonts, attributes, or colors), but you can set the font in
which the text will be displayed with the Font property,aswellasthecontrol’sbackgroundcolor
with the BackColor property.
MultiLine
This property determines whether the TextBox control will hold a single line or multiple lines
of text. Every time you place a TextBox control on your form, it’s sized for a single line of text
and you can change its width only. To change this behavior, set the MultiLine property to True.
When creating multiline TextBoxes, you will most likely have to set one or more of the MaxLength,
ScrollBars,and WordWrap properties in the Properties window.
MaxLength
This property determines the number of characters that the TextBox control will accept. Its default
value is 32,767, which was the maximum number of characters the VB 6 version of the control
could hold. Set this property to zero, so that the text can have any length, up to the control’s
capacity limit — 2,147,483,647 characters, to be exact. To restrict the number of characters that the
user can type, set the value of this property accordingly.
The MaxLength property of the TextBox control is often set to a specific value in data-entry
applications, which prevents users from entering more characters than can be stored in a database
field. A TextBox control for entering international standard book numbers (ISBNs), for instance,
shouldn’t accept more than 13 characters.Petroutsos c06.tex V3 - 01/28/2008 12:50pm Page 175
THE TEXTBOX CONTROL 175
ScrollBars
This property lets you specify the scroll bars you want to attach to the TextBox if the text exceeds
the control’s dimensions. Single-line text boxes can’t have a scroll bar attached, even if the text
exceeds the width of the control. Multiline text boxes can have a horizontal or a vertical scroll bar,
or both.
If you attach a horizontal scroll bar to the TextBox control, the text won’t wrap automatically
as the user types. To start a new line, the user must press Enter. This arrangement is useful for
implementing code editors in which lines must break explicitly. If the horizontal scroll bar is
missing, the control inserts soft line breaks when the text reaches the end of a line, and the text is
wrapped automatically. You can change the default behavior by setting the WordWrap property.
WordWrap
This property determines whether the text is wrapped automatically when it reaches the right
edge of the control. The default value of this property is True. If the control has a horizontal scroll
bar, however, you can enter very long lines of text. The contents of the control will scroll to the
left, so the insertion point is always visible as you type. You can turn off the horizontal scroll bar
and still enter long lines of text; just use the left/right arrows to bring any part of the text into
view. You can experiment with the WordWrap and ScrollBars properties in the TextPad sample
application, which is described later in this chapter.
Notice that the WordWrap property has no effect on the actual line breaks. The lines are wrapped
automatically, and there are no hard breaks (returns) at the end of each line. Open the TextPad
project, enter a long paragraph, and resize the window — the text is automatically adjusted to the
new width of the control.
A Functional Text Editor by Design
A TextBox control with its MaxLength property set to 0, its MultiLine and WordWrap properties set
to True, and its ScrollBars property set to Vertical is, on its own, a functional text editor. Place a
TextBox control with these settings on a form, run the application, and check out the following:
◆ Enter text and manipulate it with the usual editing keys: Delete, Insert, Home, and End.
◆ Select multiple characters with the mouse or the arrow keys while holding down the
Shift key.
◆ Move segments of text around with Copy (Ctrl+C), Cut (Ctrl+X), and Paste (Ctrl+V, or Shift+
Insert) operations.
◆ Right-click the control to see its context menu; it contains all the usual text-editing commands.
◆ Exchange data with other applications through the Clipboard.
You can do all this without a single line of code! If you use the My object, you can save and load files
by using two lines of code. Shortly, you’ll see what you can do with the TextBox control if you add
some code to your application, but first let’s continue our exploration of the properties that allow us
to manipulate the control’s functionality.
AcceptsReturn, AcceptsTab
These two properties specify how the TextBox control reacts to the Return (Enter) and Tab keys.
The Enter key activates the default button on the form, if there is one. The default button is usuallyPetroutsos c06.tex V3 - 01/28/2008 12:50pm Page 176
176 CHAPTER 6 BASIC WINDOWS CONTROLS
an OK button that can be activated with the Enter key, even if it doesn’t have the focus. In a
multiline TextBox control, however, we want to be able to use the Enter key to change lines. The
default value of the AcceptsReturn property is True, so pressing Enter creates a new line on the
control. If you set it to False, users can still create new lines in the TextBox control, but they’ll
have to press Ctrl+Enter. If the form contains no default button, the Enter key creates a new line
regardless of the AcceptsReturn setting.
Likewise, the AcceptsTab property determines howthe control reacts to the Tab key.Normally,
the Tab key takes you to the next control in the Tab order, and we generally avoid changing the
default setting of the AcceptsTab property. In a multiline TextBox control, however, you may
want the Tab key to insert a Tab character in the text of the control instead; to do this, set the
control’s AcceptsTab property to True (the default value is False). If you change the default value,
users can still move to the next control in the Tab order by pressing Ctrl+Tab. Notice that the
AcceptsTab property has no effect on other controls. Users may have to press Ctrl+Tab to move
to the next control while a TextBox control has the focus, but they can use the Tab key to move
from any other control to the next one.
CharacterCasing
This property tells the control to change the casing of the characters as they’re entered by the user.
Its default value is Normal, and characters are displayed as typed. You can set it to Upper or Lower
to convert the characters to upper- or lowercase automatically.
PasswordChar
This property turns the characters typed into any character you specify. If you don’t want to
display the actual characters typed by the user (when entering a password, for instance), use this
property to define the character to appear in place of each character the user types.
The default value of this property is an empty string, which tells the control to display the
characters as entered. If you set this value to an asterisk (*), for example, the user sees an asterisk
in the place of every character typed. This property doesn’t affect the control’s Text property,
which contains the actual characters. If the PasswordChar property is set to any character, the user
can’t copy or cut the text on the control.
ReadOnly, Locked
If you want to display text on a TextBox control but prevent users from editing it (such as for an
agreement or a contract they must read, software installation instructions, and so on), you can set
the ReadOnly property to True.When ReadOnly is set to True, you can put text on the control from
within your code, and users can view it, yet they can’t edit it.
To prevent editing of the TextBox control with VB 6, you had to set the Locked property to
True. Oddly, the Locked property is also supported, but now it has a very different function. The
Locked property of VB 2008 locks the control at design time (so that you won’t move it or change
its properties by mistake as you design the form).
Text-Manipulation Properties
Most of the properties for manipulating text in a TextBox control are available at runtime only.
This section presents a breakdown of each property.Petroutsos c06.tex V3 - 01/28/2008 12:50pm Page 177
THE TEXTBOX CONTROL 177
Text
Themost important property of the TextBox control is the Text property,which holds the control’s
text. You can set this property at design time to display some text on the control initially.
Notice that there are two methods of setting the Text property at design time. For single-line
TextBox controls, set the Text property to a short string, as usual. For multiline TextBox controls,
open the Lines property and enter the text in the String Collection Editor window, which will
appear. In this window, each paragraph is entered as a single line of text. When you’re finished,
click OK to close the window; the text you entered in the String Collection Editor window will
be placed on the control. Depending on the width of the control and the setting of the WordWrap
property, paragraphs may be broken into multiple lines.
At runtime, use the Text property to extract the text entered by the user or to replace
the existing text. The Text property is a string and can be used as an argument with the usual
string-manipulation functions of Visual Basic. You can also manipulate it with the members of the
String class. The following expression returns the number of characters in the TextBox1 control:
Dim strLen As Integer = TextBox1.Text.Length
The IndexOf method of the String class will locate a specific string in the control’s text.
The following statement returns the location of the first occurrence of the string Visual
in the text:
Dim location As Integer
location = TextBox1.Text.IndexOf(”Visual”)
Formore information on locating strings in a TextBox control, see the section ‘‘VB 2008 atWork:
The TextPad Project’’ later in this chapter, where we’ll build a text editor with search-and-replace
capabilities. For a detailed discussion of the String class, see Chapter 13, ‘‘Handling Strings,
Characters, and Dates.’’
To store the control’s contents in a file, use a statement such as the following:
StrWriter.Write(TextBox1.Text)
Similarly, you can read the contents of a text file into a TextBox control by using a statement
such as the following:
TextBox1.Text = StrReader.ReadToEnd
where StrReader and StrWriter are two properly declared StreamReader and StreamWriter
variables. File operations are discussed in detail in Chapter 15, ‘‘Accessing Folders and Files.’’ You
will also find out how to print text files in Chapter 20, ‘‘Printing with Visual Basic 2008.’’
To locate all instances of a string in the text, use a loop like the one in Listing 6.1. This loop
locates successive instances of the string Basic and then continues searching from the character
following the previous instance of the word in the text. To locate the last instance of a string in the
text, use the LastIndexOf method. You can write a loop similar to the one in Listing 6.1 that scans
the text backward.Petroutsos c06.tex V3 - 01/28/2008 12:50pm Page 178
178 CHAPTER 6 BASIC WINDOWS CONTROLS
Listing 6.1: Locating All Instances of a String in a TextBox
Dim startIndex = -1
startIndex = TextBox1.Text.IndexOf(”Basic”, startIndex + 1)
While startIndex > 0
Console.WriteLine ”String found at ” & startIndex
startIndex = TextBox1.Text.IndexOf(”Basic”, startIndex + 1)
End While
To test this code segment, place a multiline TextBox and a Button control on a form; then enter
the statements of the listing in the button’s Click event handler. Run the application and enter
some text on the TextBox control. Make sure that the text contains the word Basic or change the
code to locate another word, and click the button. Notice that the IndexOf method performs a
case-sensitive search.
Use the Replace method to replace a string with another within the line, the Split method
to split the line into smaller components (such as words), and any other method exposed by the
String class to manipulate the control’s text. The following statement appends a string to the
existing text on the control:
TextBox1.Text = TextBox1.Text & newString
This statement has appeared in just about any VB 6 application that manipulated text with the
TextBox control. It is an inefficient method to append text to the control, especially if the control
contains a lot of text already.
Now, you can use the AppendText method to append strings to the control, which is far more
efficient than manipulating the Text property directly. To append a string to a TextBox control,
use the following statement:
TextBox1.AppendText(newString)
The AppendText method appends the specified text to the control as is, without any line breaks
between successive calls. If you want to append individual paragraphs to the control’s text, you
must insert the line breaks explicitly, with a statement such as the following (vbCrLf is a constant
for the carriage return/new line characters):
TextBox1.AppendText(newString & vbCrLf)
Lines
In addition to the Text property, you can access the text on the control by using the Lines
property. The Lines property is a string array, and each element holds a paragraph of text. The
first paragraph is stored in the element Lines(0), the second paragraph in the element Lines(1),
and so on. You can iterate through the text lines with a loop such as the following:
Dim iLine As Integer
For iLine = 0 To TextBox1.Lines.GetUpperBound(0) - 1
{ process string TextBox1.Lines(iLine) }
NextPetroutsos c06.tex V3 - 01/28/2008 12:50pm Page 179
THE TEXTBOX CONTROL 179
You must replace the line in brackets with the appropriate code, of course. Because the Lines
property is an array, it supports the GetUpperBound method, which returns the index of the last
element in the array. Each element of the Lines array is a string, and you can call any of the String
class’s methods to manipulate it. Just keep in mind that you can’t alter the text on the control by
editing the Lines array. However, you can set the control’s text by assigning an array of strings to
the Lines property.
Text-Selection Properties
The TextBox control provides three properties for manipulating the text selected by the user:
SelectedText, SelectionStart,and SelectionLength. Users can select a range of text with a
click-and-drag operation, and the selected text will appear in reverse color. You can access the
selected text from within your code through the SelectedText property, and its location in
the control’s text through the SelectionStart and SelectionLength properties.
SelectedText
This property returns the selected text, enabling you to manipulate the current selection from
within your code. For example, you can replace the selection by assigning a new value to the
SelectedText property. To convert the selected text to uppercase, use the ToUpper method of
the String class:
TextBox1.SelectedText = TextBox1.SelectedText.ToUpper
SelectionStart, SelectionLength
Use these two properties to read the text selected by the user on the control, or to select text from
within your code. The SelectionStart property returns or sets the position of the first character
of the selected text, somewhat like placing the cursor at a specific location in the text and selecting
text by dragging the mouse. The SelectionLength property returns or sets the length of the
selected text.
Suppose that the user is seeking the word Visual in the control’s text. The IndexOf method
locates the string but doesn’t select it. The following statements select the word in the text,
highlight it, and bring it into view, so that users can spot it instantly:
Dim seekString As String = ”Visual”
Dim strLocation As Long
strLocation = TextBox1.Text.IndexOf(seekString)
If strLocation > 0 Then
TextBox1.SelectionStart = strLocation
TextBox1.SelectionLength = seekString.Length
End If
TextBox1.ScrollToCaret()
These lines locate the string Visual (or any user-supplied string stored in the seekString
variable) in the text and select it by setting the SelectionStart and SelectionLength properties
of the TextBox control. If the located string lies outside the visible area of the control, the user must
scroll the text to bring the selection into view. The TextBox control provides the ScrollToCaret
method, which brings the section of the text with the cursor (the caret position)intoview.Petroutsos c06.tex V3 - 01/28/2008 12:50pm Page 180
180 CHAPTER 6 BASIC WINDOWS CONTROLS
The few lines of code shown previously form the core of a text editor’s Find command.
Replacing the current selection with another string is as simple as assigning a new value to the
SelectedText property, and this technique provides you with an easy implementation of a Find
and Replace operation.
Locating the Cursor Position in the Control
The SelectionStart and SelectionLength properties always have a value even if no text is selected
on the control. In this case, SelectionLength is 0, and SelectionStart is the current position of
the pointer in the text. If you want to insert some text at the pointer’s location, simply assign it to the
SelectedText property, even if no text is selected on the control.
HideSelection
The selected text in the TextBox does not remain highlighted when the user moves to another
control or form; to change this default behavior, set the HideSelection property to False. Use this
property to keep the selected text highlighted, even if another form or a dialog box, such as a Find
& Replace dialog box, has the focus. Its default value is True, which means that the text doesn’t
remain highlighted when the TextBox loses the focus.
Text-Selection Methods
In addition to properties, the TextBox control exposes two methods for selecting text. You can
select some text by using the Select method, whose syntax is shown next:
TextBox1.Select(start, length)
The Select method is equivalent to setting the SelectionStart and SelectionLength prop-
erties. To select the characters 100 through 105 on the control, call the Select method, passing the
values 99 and 6 as arguments:
TextBox1.Select(99, 6)
As a reminder, the order of the characters starts at 0 (the first character’s index is 0, the second
character’s index is 1, and the last character’s index is the length of the string minus 1).
If the range of characters you select contains hard line breaks, you must take them into
consideration as well. Each hard line break counts for two characters (carriage return and line
feed). If the TextBox control contains the string ABCDEFGHI, the following statement will select the
range DEFG:
TextBox1.Select(3, 4)
If you insert a line break every third character and the text becomes the following, the same
statement will select the characters DE only:
ABC
DEF
GHIPetroutsos c06.tex V3 - 01/28/2008 12:50pm Page 181
THE TEXTBOX CONTROL 181
In reality, it has also selected the two characters that separate the first two lines, but special
characters aren’t displayed and can’t be highlighted. The length of the selection, however, is 4.
A variation of the Select method is the SelectAll method, which selects all the text on
the control.
Undoing Edits
An interesting feature of the TextBox control is that it can automatically undo the most recent
edit operation. To undo an operation from within your code, you must first examine the value of
the CanUndo property. If it’s True, the control can undo the operation; then you can call the Undo
method to undo the most recent edit.
An edit operation is the insertion or deletion of characters. Entering text without deleting any
is considered a single operation and will be undone in a single step. Even if the user has spent
an hour entering text (without making any corrections), you can make all the text disappear with
asinglecalltothe Undo method. Fortunately, the deletion of the text becomes the most recent
operation, which can be undone with another call to the Undo method. In effect, the Undo method
is a toggle. When you call it for the first time, it undoes the last edit operation. If you call it again,
it redoes the operation it previously undid. The deletion of text can be undone only if no other
editing operation has taken place in the meantime. You can disable the redo operation by calling
the ClearUndo method, which clears the undo buffer of the control. You should call it from within
an Undo command’s event handler to prevent an operation from being redone. In most cases, you
should give users the option to redo an operation, especially because the Undo method can delete
an enormous amount of text from the control.
VB 2008 at Work: The TextPad Project
The TextPad application, shown in Figure 6.2, demonstrates most of the TextBox control’s proper-
ties and methods described so far. TextPad is a basic text editor that you can incorporate into your
programs and customize for special applications. The TextPad project’s main form is covered by a
TextBox control, whose size is adjusted every time the user resizes the form. This feature doesn’t
require any programming — just set the Dock property of the TextBox control to Fill.
Figure 6.2
TextPad demonstrates
the most useful
properties and methods
of the TextBox control.
The name of the application’s main form is frmTextPad, and the name of the Find & Replace
dialog box is frmFind. You can design the two forms as shown in the figures of this chapter, orPetroutsos c06.tex V3 - 01/28/2008 12:50pm Page 182
182 CHAPTER 6 BASIC WINDOWS CONTROLS
open the TextPad project. To design the application’s interface from scratch, place a MenuStrip
control on the form and dock it to the top of the form. Then place a TextBox control on the main
form, name it txtEditor, and set the following properties: Multiline to True, MaxLength to 0
(to edit text documents of any length), HideSelection to False (so that the selected text remains
highlighted even when the main form doesn’t have the focus), and Dock to Fill, so that it will fill
the form.
The menu bar of the form contains all the commands you’d expect to find in text-editing
applications; they’re listed in Table 6.1.
Table 6.1: The TextPad Form’s Menu
Menu Command Description
File New Clears the text
Open Loads a new text file from disk
Save Saves the text to its file on disk
Save As Saves the text with a new filename on disk
Print Prints the text
Exit Terminates the application
Edit Undo/Redo Undoes/redoes the last edit operation
Copy Copies selected text to the Clipboard
Cut Cuts the selected text
Paste Pastes the Clipboard’s contents to the editor
Select All Selects all text in the control
Find & Replace Displays a dialog box with Find and Replace options
Process Convert To Upper Converts selected text to uppercase
Convert To Lower Converts selected text to lowercase
Number Lines Numbers the text lines
Format Font Sets the text’s font, size, and attributes
Page Color Sets the control’s background color
Text Color Sets the color of the text
WordWrap Toggle menu item that turns text wrapping on and off
The File menu commands are implemented with the Open and Save As dialog boxes, the
Font command with the Font dialog box, and the Color command with the Color dialog box.
These dialog boxes are discussed in the following chapters, and as you’ll see, you don’t have toPetroutsos c06.tex V3 - 01/28/2008 12:50pm Page 183
THE TEXTBOX CONTROL 183
design them yourself. All you have to do is place a control on the form and set a few properties;
the Framework takes it from there. The application will display the standard Open File/Save
File/Font/Color dialog boxes, in which the user can select or specify a filename or select a font
or color. Of course, we’ll provide a few lines of code to actually move the text into a file (or read
it from a file and display it on the control), change the control’s background color, and so on. I’ll
discuss the commands of the File menu in Chapter 8, ‘‘MoreWindows Controls.’’
The Editing Commands
The options on the Edit menu move the selected text to and from the Clipboard. For the TextPad
application, all you need to know about the Clipboard are the SetText method, which places the
currently selected text on the Clipboard, and the GetText method, which retrieves information
from the Clipboard (see Figure 6.3).
Figure 6.3
The Copy, Cut, and
Paste operations can
be used to exchange text
with any other
application.
The Copy command, for example, is implemented with a single line of code (txtEditor is the
name of the TextBox control). The Cut command does the same, and it also clears the selected text.
The code for these and for the Paste command, which assigns the contents of the Clipboard to the
current selection, is presented in Listing 6.2.
Listing 6.2: The Cut, Copy, and Paste Commands
Private Sub EditCopyItem Click(...)
Handles EditCopyItem.Click
If txtEditor.SelectionLength > 0 Then
Clipboard.SetText(txtEditor.SelectedText)
End If
End SubPetroutsos c06.tex V3 - 01/28/2008 12:50pm Page 184
184 CHAPTER 6 BASIC WINDOWS CONTROLS
Private Sub EditCutItem Click(...)
Handles EditCutItem.Click
Clipboard.SetText(txtEditor.SelectedText)
txtEditor.SelectedText = ””
End Sub
Private Sub EditPasteItem Click(...)
Handles EditPasteItem.Click
If Clipboard.ContainsText Then
txtEditor.SelectedText = Clipboard.GetText
End If
End Sub
If no text is currently selected, the Clipboard’s text is pasted at the pointer’s current location.
If the Clipboard contains a bitmap (placed there by another application) or any other type of
data that the TextBox control can’t handle, the paste operation will fail; that’s why we handle the
Paste operation with an If statement. You could provide some hint to the user by including an
Else clause that informs them that the data on the Clipboard can’t be used with a text-editing
application.
The Process and Format Menus
The commands of the Process and Format menus are straightforward. The Format menu
commands open the Font or Color dialog box and change the control’s Font, ForeColor,and
BackColor properties. You will learn how to use these controls in the following chapter. The
Upper Case and Lower Case commands of the Process menu are also trivial: they select all the text,
convert it to uppercase or lowercase, respectively, and assign the converted text to the control’s
SelectedText property with the following statements:
txtEditor.SelectedText = txtEditor.SelectedText.ToLower
txtEditor.SelectedText = txtEditor.SelectedText.ToUpper
Notice that the code uses the SelectedText property to convert only the selected text, not
the entire document. The Number Lines command inserts a number in front of each text line
and demonstrates how to process the individual lines of text on the control. However, it doesn’t
remove the line numbers, and there’s no mechanism to prevent the user from editing the line
numbers or inserting/deleting lines after they have been numbered. Use this feature to create a
numbered listing or to number the lines of a file just before saving it or sharing it with another user.
Listing 6.3 shows the Number Lines command’s code and demonstrates how to iterate through
the TextBox control’s Lines array.
Listing 6.3: The Number Lines Command
Private Sub ProcessNumberLinesItem Click(...)
Handles ProcessNumberLines.Click
Dim iLine As Integer
Dim newText As New System.Text.StringBuilder()
For iLine = 0 To txtEditor.Lines.Length - 1Petroutsos c06.tex V3 - 01/28/2008 12:50pm Page 185
THE TEXTBOX CONTROL 185
newText.Append((iLine + 1).ToString & vbTab &
txtEditor.Lines(iLine) & vbCrLf)
Next
txtEditor.SelectAll()
Clipboard.SetText(newText.ToString)
txtEditor.Paste()
End Sub
This event handler uses a StringBuilder variable. The StringBuilder class, which is discussed
in detail in Chapter 12, ‘‘Designing Custom Windows Controls,’’ is equivalent to the String class;
it exposes similar methods and properties, but it’s much faster at manipulating dynamic strings
than the String class.
Search and Replace Operations
The last option in the Edit menu — and the most interesting — displays a Find & Replace dialog
box (shown in Figure 6.2). This dialog box works like the similarly named dialog box of Microsoft
Word and many other Windows applications. The buttons in the Find & Replace dialog box are
relatively self-explanatory:
Find The Find command locates the first instance of the specified string in the text after
the cursor location. If a match is found, the Find Next, Replace, and Replace All buttons
are enabled.
Find Next This command locates the next instance of the string in the text. Initially,
this button is disabled; it’s enabled only after a successful Find operation.
Replace This replaces the current selection with the replacement string and then locates the
next instance of the same string in the text. Like the Find Next button, it’s disabled until a
successful Find operation occurs.
Replace All This replaces all instances of the string specified in the Search For box with the
string in the Replace With box.
Design a form like the one shown in Figure 6.2 and set its TopMost property to True. We want
this form to remain on top of the main form, even when it doesn’t have the focus.
Whether the search is case-sensitive or not depends on the status of the Case Sensitive
CheckBox control. If the string is found in the control’s text, the program highlights it by
selecting it. In addition, the program calls the TextBox control’s ScrollToCaret method to bring
the selection into view. The Find Next button takes into consideration the location of the pointer
and searches for a match after the current location. If the user moves the pointer somewhere
else and then clicks the Find Next button, the program will locate the first instance of the string
after the current location of the pointer — and not after the last match. Of course, you can always
keep track of the location of each match and continue the search from this location. The Find
button executes the code shown in Listing 6.4.
Listing 6.4: The Find Button
Private Sub bttnFind Click(...) Handles bttnFind.Click
Dim selStart As Integer
If chkCase.Checked = True Then
selStart =Petroutsos c06.tex V3 - 01/28/2008 12:50pm Page 186
186 CHAPTER 6 BASIC WINDOWS CONTROLS
frmTextPad.txtEditor.Text.IndexOf(
searchWord.Text, StringComparison.Ordinal)
Else
selStart =
frmTextPad.txtEditor.Text.IndexOf(
searchWord.Text,
StringComparison.OrdinalIgnoreCase)
End If
If selStart = -1 Then
MsgBox(”Can’t find word”)
Exit Sub
End If
frmTextPad.txtEditor.Select(
selStart, searchWord.Text.Length)
bttnFindNext.Enabled = True
bttnReplace.Enabled = True
bttnReplaceAll.Enabled = True
frmTextPad.txtEditor.ScrollToCaret()
End Sub
The Find button examines the value of the chkCase CheckBox control, which specifies whether
the search will be case-sensitive and calls the appropriate form of the IndexOf method. The first
argument of this method is the string we’re searching for; the second argument is the search mode,
and its value is a member of the StringComparison enumeration: Ordinal for case-sensitive
searches and OrdinalIgnoreCase for case-insensitive searches. If the IndexOf method locates
the string, the program selects it by calling the control’s Select method with the appropriate
arguments. If not, it displays a message. Notice that after a successful Find operation, the Find
Next, Replace, and Replace All buttons on the form are enabled.
The code of the Find Next button is the same, but it starts searching at the character following
the current selection. This way, the IndexOf method locates the next instance of the same string.
Here’s the statement that locates the next instance of the search argument:
selStart = frmTextPad.txtEditor.Text.IndexOf(
searchWord.Text,
frmTextPad.txtEditor.SelectionStart + 1,
StringComparison.Ordinal)
The Replace button replaces the current selection with the replacement string and then locates
the next instance of the find string. The Replace All button replaces all instances of the search word
in the document. Listing 6.5 presents the code behind the Replace and Replace All buttons.
Listing 6.5: The Replace and Replace All Operations
Private Sub bttnReplace Click(...)
Handles bttnReplace.Click
If frmTextPad.txtEditor.SelectedText <> ”” Then
frmTextPad.txtEditor.SelectedText = replaceWord.Text
End If
bttnFindNext Click(sender, e)
End SubPetroutsos c06.tex V3 - 01/28/2008 12:50pm Page 187
THE TEXTBOX CONTROL 187
Private Sub bttnReplaceAll Click(...)
Handles bttnReplaceAll.Click
Dim curPos, curSel As Integer
curPos = frmTextPad.txtEditor.SelectionStart
curSel = frmTextPad.txtEditor.SelectionLength
frmTextPad.txtEditor.Text =
frmTextPad.txtEditor.Text.Replace(
searchWord.Text.Trim, replaceWord.Text.Trim)
frmTextPad.txtEditor.SelectionStart = curPos
frmTextPad.txtEditor.SelectionLength = curSel
End Sub
The Replace method is case-sensitive, which means that it replaces instances of the search
argument in the text that have the exact same spelling as its first argument. For a case-insensitive
replace operation, you must write the code to perform consecutive case-insensitive search-
and-replace operations. Alternatively, you can use the Replace built-in function to perform
case-insensitive searches. Here’s how you’d call the Replace function to performa case-insensitive
replace operation:
Replace(frmTextPad.txtEditor.Text, searchWord.Text.Trim,
replaceWord.Text.Trim, , , CompareMethod.Text)
The last, optional, argument determines whether the search will be case-sensitive
(CompareMethod.Binary) or case-insensitive (CompareMethod.Text).
The Undo/Redo Commands
The Undo command (shown in Listing 6.6) is implemented with a call to the Undo method.
However, because the Undo method works like a toggle, we must also toggle its caption from
Undo to Redo (and vice versa) each time the command is activated.
Listing 6.6: The Undo/Redo Command of the EditMenu
Private Sub EditUndoItem Click(...)
Handles EditUndoItem.Click
If EditUndoItem.Text = ”Undo” Then
If txtEditor.CanUndo Then
txtEditor.Undo()
EditUndoItem.Text = ”Redo”
End If
Else
If txtEditor.CanUndo Then
txtEditor.Undo()
EditUndoItem.Text = ”Undo”
End If
End If
End SubPetroutsos c06.tex V3 - 01/28/2008 12:50pm Page 188
188 CHAPTER 6 BASIC WINDOWS CONTROLS
If you edit the text after an undo operation, you can no longer redo the last undo operation.
This means that as soon as the contents of the TextBox control change, the caption of the first
command in the Edit menu must become Undo, even if it’s Redo at the time. The Redo command
is available only after undoing an operation and before editing the text. So, how do we know that
the text has been edited? The TextBox control fires the TextChanged event every time its contents
change. We’ll use this event to restore the caption of the Undo/Redo command to Undo. Insert
the following statement in the TextChanged event of the TextBox control:
EditUndoItem.Text = ”Undo”
The TextBox control can’t provide more-granular undo operations— unlike Word, which
keeps track of user actions (insertions, deletions, replacements, and so on) and then undoes them
in steps. If you need a more-granular undo feature, you should use the RichTextBox control, which
is discussed in detail in Chapter 8. The RichTextBox control can display formatted text, but it can
also be used as an enhanced TextBox control. By the way, setting the menu item’s caption from
within the TextChanged event handler is an overkill, because this event takes place every time the
user presses a key. However, the operation takes no time at all and doesn’t make the application
less responsive. A better choice would be the DropDownOpening event of the editFormat item,
which is fired every time the user opens the Edit menu.
Capturing Keystrokes
The TextBox control has a single unique event, the TextChanged event, which is fired every time
the text on the control is changed, either because the user has typed a character or because of
a paste operation. Another event that is quite common in programming the TextBox control
is the KeyPress event, which occurs every time a key is pressed and reports the character that
was pressed. You can use this event to capture certain keys and modify the program’s behavior
depending on the character typed.
Suppose that you want to use the TextPad application to prepare messages for transmission
over a telex line. As you may know, a telex can’t transmit lowercase characters or special symbols.
Theeditormustconvertthetexttouppercaseandreplace the special symbols with their equivalent
strings: DLR for $, AT for @, O/O for %, BPT for #, and AND for &. You can modify the default
behavior of the TextBox control fromwithin the KeyPress event so that it converts these characters
as the user types.
By capturing keystrokes, you can process the data as they are entered, in real time. For example,
you can make sure that a TextBox accepts only numeric or hexadecimal characters and rejects all
others. To implement an editor for preparing text for telex transmission, use the KeyPress event
handler shown in Listing 6.7.
Listing 6.7: Handling Keystrokes for a TELEXmessage
Private Sub txtEditor KeyPress(
ByVal sender As Object,
ByVal e As System.Windows.
Forms.KeyPressEventArgs)
Handles txtEditor.KeyPress
If System.Char.IsControl(e.KeyChar) Then Exit Sub
Dim ch As Char = Char.ToUpper(e.KeyChar)Petroutsos c06.tex V3 - 01/28/2008 12:50pm Page 189
THE TEXTBOX CONTROL 189
Select Case ch.ToString
Case ”@”
txtEditor.SelectedText = ”AT”
Case ”#”
txtEditor.SelectedText = ”BPT”
Case ”$”
txtEditor.SelectedText = ”DLR”
Case ”%”
txtEditor.SelectedText = ”O/O”
Case ”&”
txtEditor.SelectedText = ”AND”
Case Else
txtEditor.SelectedText = ch
End Select
e.Handled = True
End Sub
The very first executable statement in the event handler examines the key that was pressed and
exits if it is a special editing key (Delete, Backspace, Ctrl+V, and so on). If so, the handler exits
without taking any action. The KeyChar property of the e argument of the KeyPress event reports
the key that was pressed. The code converts it to a string and then uses a Case statement to handle
individual keystrokes. If the user pressed the $ key, for example, the code displays the characters
DLR. If no special character was pressed, the code displays the character pressed as is from within
the Case Else clause of the Select statement.
Cancelling Keystrokes
Before you exit the event handler, you must ‘‘kill’’ the original key pressed, so that it won’t appear
on the control. You do this by setting the Handled property to True, which tells VB that it shouldn’t
process the keystroke any further. If you omit this statement, the special characters will be printed
twice: once in their transformed format (DLR$, AT@, and so on) and once as regular characters. You
can also set the SuppressKeyPress property to True to cancel a keystroke; the Common Language
Runtime (CLR) will not pass the keystroke to the appropriate control.
Capturing Function Keys
Another common feature in text-editing applications is the assignment of special operations to
the function keys. The Notepad application, for example, uses the F5 function key to insert the
current date at the cursor’s location. You can do the same with the TextPad application, but you
can’t use the KeyPress event —the KeyChar argument doesn’t report function keys. The events
that can capture the function keys are the KeyDown and KeyUp events. Also, unlike the KeyPress
event, these two events don’t report the character pressed, but instead report the key’s code (a
special number that distinguishes each key on the keyboard, also known as the scancode), through
the e.KeyCode property.
The keycode is unique for each key, not each character. Lower- and uppercase characters have
different ASCII values but the same keycode because they are on the same key. For example,
the number 4 and the $ symbol have the same keycode because the same key on the keyboardPetroutsos c06.tex V3 - 01/28/2008 12:50pm Page 190
190 CHAPTER 6 BASIC WINDOWS CONTROLS
generates both characters. When the key’s code is reported, the KeyDown and KeyUp events
also report the state of the Shift, Ctrl, and Alt keys through the e.Shift, e.Alt,and
e.Control properties.
The KeyUp event handler shown in Listing 6.8 uses the F5 and F6 function keys to insert the
current date and time in the document. It also uses the F7 and F8 keys to insert two predefined
strings in the document.
Listing 6.8: KeyUp Event Examples
Private Sub txtEditor KeyUp(ByVal sender As Object,
ByVal e As System.Windows.Forms.KeyEventArgs)
Handles txtEditor.KeyUp
Select Case e.KeyCode
Case Keys.F5 :
txtEditor.SelectedText =
Now().ToLongDateString
Case Keys.F6 :
txtEditor.SelectedText =
Now().ToLongTimeString
Case Keys.F7 :
txtEditor.SelectedText =
”MicroWeb Designs, Inc.”
Case Keys.F8 :
txtEditor.SelectedText =
”Another user-supplied string”
End Select
End Sub
Windows already uses many of the function keys (for example, the F1 key for help), and you
shouldn’tmodify their original functions.With a little additional effort, you can provide userswith
a dialog box that lets them assign their own strings to function keys. You’ll probably have to take
into consideration the status of the Shift, Control,and Alt properties of the event’s e argument,
which report the status of the Shift, Ctrl, and Alt keys, respectively. To find out whether two of the
modifier keys are pressed along with a key, use the AND operator with the appropriate properties
of the e argument. The following If clause detects the Ctrl and Alt keys:
If e.Control AND e.Alt Then
{ Both Alt and Control keys were down}
End If
Auto-complete Properties
One set of interesting properties of the TextBox control are the autocomplete properties. Have
you noticed how Internet Explorer prompts you with possible matches as soon as you startPetroutsos c06.tex V3 - 01/28/2008 12:50pm Page 191
THE TEXTBOX CONTROL 191
typing an address or your username in a text box (or in the address bar of the browser)? You can
easily implement such boxes with a single-line TextBox control and the autocomplete properties.
Basically, you have to tell the TextBox control how it should prompt the user with strings that
match the characters already entered on the control and where the matches will come from. Then,
the control can display in a drop-down list the strings that begin with the characters already typed
by the user. The user can either continue typing (in which case the list of options becomes shorter)
or select an item from the list. The autocomplete properties apply to single-line TextBox controls
only; they do not take effect on multiline TextBox controls.
In many cases, an autocomplete TextBox control is more functional than a ComboBox con-
trol, and you should prefer it. You will see that the ComboBox also supports the autocomplete
properties, because they make the control so much easier to use only with the keyboard.
The AutoCompleteMode property determines whether, and how, the TextBox control will
prompt users, and its setting is a member of the AutoCompleteMode enumeration (AutoSuggest,
AutoAppend, AutoSuggestAppend,and None). In AutoAppend mode, the TextBox control selects
the first matching item in the list of suggestions and completes the text. In AutoSuggestAppend
mode, the control suggests the first matching item in the list, as before, but it also expands the list.
In AutoSuggest mode, the control simply opens a list with the matching items but doesn’t select
any of them. Regular TextBox controls have their AutoCompleteMode property set to False.
The AutoCompleteSource property determines where the list of suggestions comes from; its
value is a member of the AutoCompleteSource enumeration, which is shown in Table 6.2.
Table 6.2: The Members of the AutoCompleteSource
Member Description
AllSystemSources The suggested items are the names of system resources.
AllUrl The suggested items are the URLs visited by the target computer. Does not
work if you’re deleting the recently viewed pages.
CustomSource The suggested items come from a custom collection.
FileSystem The suggested items are filenames.
HistoryList The suggested items come from the computer’s history list.
RecentlyUsedList The suggested items come from the Recently Used folder.
None The control doesn’t suggest any items.
To demonstrate the basics of the autocomplete properties, I’ve included the AutoComplete-
TextBoxes project, whose main form is shown in Figure 6.4. This project allows you to set the
autocomplete mode and source for a single-line TextBox control. The top TextBox control uses
a custom list of words, while the lower one uses one of the built-in autocomplete sources (file
system, URLs, and so on).Petroutsos c06.tex V3 - 01/28/2008 12:50pm Page 192
192 CHAPTER 6 BASIC WINDOWS CONTROLS
Figure 6.4
Suggesting words with
the AutoComplete-
Source property
If you set the AutoCompleteSource to CustomSource, you must also populate an
AutoCompleteStringCollection object with the desired suggestions and assign it to
the AutoCompleteCustomSource property. The AutoCompleteStringCollection is just a
collection of strings. Listing 6.9 shows statements in a form’s Load event that prepare such a list
and use it with the TextBox1 control.Petroutsos c06.tex V3 - 01/28/2008 12:50pm Page 193
THE TEXTBOX CONTROL 193
Listing 6.9: Populating a CustomAutoCompleteSource Property
Private Sub Form1 Load(...)
Handles MyBase.Load
Dim knownWords As New AutoCompleteStringCollection
knownWords.Add(”Visual Basic 2008”)
knownWords.Add(”Visual Basic .NET”)
knownWords.Add(”Visual Basic 6”)
knownWords.Add(”Visual Basic”)
knownWords.Add(”Framework”)
TextBox1.AutoCompleteCustomSource = knownWords
TextBox1.AutoCompleteSource = AutoCompleteSource.CustomSource
TextBox1.AutoCompleteMode =
AutoCompleteMode.Suggest
TextBox2.AutoCompleteSource =
AutoCompleteSource.RecentlyUsedList
TextBox2.AutoCompleteMode =
AutoCompleteMode.Suggest
End Sub
The TextBox1 control on the form will open a drop-down list with all possible matches in the
knownWords collection as soon as the user starts typing in the control, as shown in the top part of
Figure 6.4. To see the autocomplete properties in action, open the AutoCompleteTextBoxes project
and examine its code. The main form of the application, shown in Figure 6.4, allows you to change
the AutoCompleteMode property of both TextBox controls on the form, and the AutoComplete-
Source property of the bottom TextBox control. The first TextBox uses a list of custom words,
which is set up when the form is loaded, with the statements in Listing 6.9.
Real-World Data-Entry Applications
Typical business applications contain numerous forms for data entry, and the most common element
on data-entry forms is the TextBox control. Data-entry operators are very efficient with the keyboard
and they should be able to use your application without reaching for the mouse.
Seasoned data-entry operators can’t live without the Enter key; they reach for this key at the end of
each operation. In my experience, a functional interface should add intelligence to this keystroke: the
Enter key should perform the ‘‘obvious’’ or ‘‘most likely’’ operation at any time.When entering data,
for example, it should take the user to the next control in the Tab order. Consider a data-entry screen
like the one shown in the following image, which contains several TextBox controls, a DataTimePicker
control for entering dates, and two CheckBox controls. This is the main form of the Simple Data Entry
Form sample project, which you will find along with the other chapter’s projects.Petroutsos c06.tex V3 - 01/28/2008 12:50pm Page 194
194 CHAPTER 6 BASIC WINDOWS CONTROLS
The application uses the Enter key intelligently: every time the Enter key is pressed, the focus ismoved
to the next control in the Tab order. Even if the current control is a CheckBox, this keystroke doesn’t
change the status of the CheckBox controls; it simply moves the focus forward.
You could program the KeyUp event of each control to react to the Enter key, but this approach can
lead tomaintenance problems if you’re going to add new controls to an existing form. The best approach
is to intercept the Enter keystroke at the form’s level, before it reaches a control. To do so, you must
set the KeyPreview property of the form to True. This setting causes the key events to be fired at the
form’s level first and then to the control that has the focus. In essence, it allows you to handle certain
keystrokes for multiple controls at once. The KeyUp event handler of the sample project’s main form
intercepts the Enter keystroke and reacts to it by moving the focus to the next control in the Tab order
via the ProcessTabKey method. This method simulates the pressing of the Tab key, and it’s called
with a single argument, which is a Boolean value: True moves the focus forward, and False moves it
backward. Here’s the code in the KeyUp event handler of the application’s form that makes the inter-
face much more functional and intuitive:
Private Sub frmDataEntry KeyUp(
ByVal sender As Object,
ByVal e As System.Windows.Forms.KeyEventArgs)
Handles Me.KeyUp
If e.KeyCode = Keys.Enter And Not (e.Alt Or e.Control) Then
If Me.ActiveControl.GetType Is GetType(TextBox) Or
Me.ActiveControl.GetType Is GetType(CheckBox) Or
Me.ActiveControl.GetType Is
GetType(DateTimePicker) Then
If e.Shift Then
Me.ProcessTabKey(False)
Else
Me.ProcessTabKey(True)
End If
End If
End If
End SubPetroutsos c06.tex V3 - 01/28/2008 12:50pm Page 195
THE LISTBOX, CHECKEDLISTBOX, AND COMBOBOX CONTROLS 195
There are a couple of things you should notice about this handler. First, it doesn’t react to the Enter
key if it was pressed along with the Alt or Ctrl keys. The Shift key, on the other hand, is used to control
the direction in the Tab order. The focus moves forward with the Enter keystroke and moves back-
ward with the Shift + Enter keystroke. Also, the focus is handled automatically only for the TextBox,
CheckBox, and DataTimePicker controls. When the user presses the Enter key when a button has the
focus, the program reacts as expected by invoking the button’s Click event handler.
The ListBox, CheckedListBox, and ComboBox Controls
The ListBox, CheckedListBox, and ComboBox controls present lists of choices, from which the
user can select one or more. The first two are illustrated in Figure 6.5.
Figure 6.5
The ListBox and
CheckedListBox controls
The ListBox control occupies a user-specified amount of space on the form and is populated
with a list of items. If the list of items is longer than can fit on the control, a vertical scroll bar
appears automatically.
The CheckedListBox control is a variation of the ListBox control. It’s identical to the ListBox
control, but a check box appears in front of each item. The user can select any number of items by
selecting the check boxes in front of them. As you know, you can also select multiple items from a
ListBox control by pressing the Shift and Ctrl keys.
The ComboBox control also contains multiple items but typically occupies less space on the
screen. The ComboBox control is an expandable ListBox control: The user can expand it to make a
selection, and collapse it after the selection is made. The real advantage of the ComboBox control,
however, is that the user can enter new information in the ComboBox, rather than being forced to
select from the items listed.
To add items at design time, locate the Items property in the control’s Properties window and
click the ellipsis button. A new window will pop up — the String Collection Editor window — in
which you can add the items you want to display in the list. Each item must appear on a separate
text line, and blank text lines will result in blank lines in the list. These items will appear in the list
when the form is loaded, but you can add more items (or remove existing ones) from within your
code at any time. They appear in the same order as entered on the String Collection Editor window
unless the control has its Sorted property set to True, in which case the items are automatically
sorted, regardless of the order in which you’ve specified them.
This section first examines the ListBox control’s properties and methods. Later, you’ll see how
the same properties and methods can be used with the ComboBox control.Petroutsos c06.tex V3 - 01/28/2008 12:50pm Page 196
196 CHAPTER 6 BASIC WINDOWS CONTROLS
Basic Properties
In this section, you’ll find the properties that determine the functionality of the three controls.
These properties are usually set at design time, but you can change their setting from within your
application’s code.
IntegralHeight
This property is a Boolean value (True/False) that indicates whether the control’s height will be
adjusted to avoid the partial display of the last item. When set to True, the control’s actual height
changes in multiples of the height of a single line, so only an integer number of rows are displayed
at all times.
Items
The Items property is a collection that holds the control’s items. At design time, you can populate
this list through the String Collection Editor window. At runtime, you can access and manipulate
the items through the methods and properties of the Items collection, which are described shortly.
MultiColumn
A ListBox control can display its items in multiple columns if you set its MultiColumn property to
True. The problem with multicolumn ListBoxes is that you can’t specify the column in which each
item will appear. ListBoxes with many items and their MultiColumn property set to True expand
horizontally, not vertically. A horizontal scroll bar will be attached to a multicolumn ListBox, so
that users can bring any column into view. This property does not apply to the ComboBox control.
SelectionMode
This property, which applies to the ListBox and CheckedListBox controls only, determines how
the user can select the list’s items. The possiblevaluesofthisproperty—membersofthe
SelectionMode enumeration— are shown in Table 6.3.
Table 6.3: The SelectionMode Enumeration
Value Description
None No selection at all is allowed.
One (Default) Only a single item can be selected.
MultiSimple Simple multiple selection: A mouse click (or pressing the spacebar) selects or deselects
an item in the list. You must click all the items you want to select.
MultiExtended Extended multiple selection: Press Shift and click the mouse (or press one of the arrow
keys) to expand the selection. This process highlights all the items between the
previously selected item and the current selection. Press Ctrl and click the mouse to
select or deselect single items in the list.
Sorted
When this property is True, the items remain sorted at all times. The default is False, because it
takes longer to insert new items in their proper location. This property’s value can be set at design
time as well as runtime.
The items in a sorted ListBox control are sorted in ascending and case-sensitive order.
Uppercase characters appear before the equivalent lowercase characters, but both upper- andPetroutsos c06.tex V3 - 01/28/2008 12:50pm Page 197
THE LISTBOX, CHECKEDLISTBOX, AND COMBOBOX CONTROLS 197
lowercase characters appear together. All words beginning with B appear after the words begin-
ning with A and before the words beginning with C. Within the group of words beginning with
B, those beginning with a capital B appear before those beginning with a lowercase b. This sorting
order is known as phone book order.
Moreover, the ListBox control won’t sort numeric data. The number 10 will appear in front of
the number 5 because the string 10 is smaller than the string 5. If the numbers are formatted as 010
and 005, they will be sorted correctly.
Text
The Text property returns the selected text on the control. Although you can set the Text property
for the ComboBox control at design time, this property is available only at runtime for the other
two controls. Notice that the items need not be strings. By default, each item is an object. For each
object, however, the control displays a string, which is the same string returned by the object’s
ToString method.
Manipulating the Items Collection
To manipulate a ListBox control from within your application, you should be able to do
the following:
◆ Add items to the list
◆ Remove items from the list
◆ Access individual items in the list
The items in the list are represented by the Items collection. You use the members of the Items
collection to access the control’s items and to add or remove items. The Items property exposes
the standard members of a collection, which are described later in this section.
Each member of the Items collection is an object. In most cases, we use ListBox controls to
store strings, but it’s possible to store objects.When you add an object to a ListBox control, a string
is displayed on the corresponding line of the control. This is the string returned by the object’s
ToString method. This is the property of the object that will be displayed by default. You can
display any other property of the object by setting the control’s ValueMember property to the
name of the property.
If you add a Color object and a Rectangle object to the Items collection with the following
statements:
ListBox1.Items.Add(New Font(”Verdana”, 12,
FontStyle.Bold)
ListBox1.Items.Add(New Rectangle(0, 0, 100, 100))
then the following strings appear on the first two lines of the control:
[Font: Name=Verdana, Size=12, Units=3, GdiCharSet=1, gdiVerticalFont=False]
{X=0, Y=0, Width=100, Height=100}
However, you can access the members of the two objects because the ListBox stores objects, not
their descriptions. The following statement prints the width of the Rectangle object (the output
produced by the statement is highlighted):
Debug.WriteLine(ListBox1.Items.Item(1).Width)
100Petroutsos c06.tex V3 - 01/28/2008 12:50pm Page 198
198 CHAPTER 6 BASIC WINDOWS CONTROLS
The expression in the preceding statement is late-bound, which means that the compiler doesn’t
know whether the first object in the Items collection is a Rectangle object and it can’t verify the
member Width. If you attempt to call the Width property of the first item in the collection, you’ll
get an exception at runtime indicating that the code has attempted to access a missing member.
The missing member is the Width property of the Font object.
The proper way to read the objects stored in a ListBox control is to examine the type of the
object first and then attempt to retrieve a property (or call a method) of the object, only if it’s of
the appropriate type. Here’s how you would read the Width property of a Rectangle object:
If ListBox1.Items.Item(0).GetType Is
GetType(Rectangle) Then
Debug.WriteLine(
CType(ListBox1.Items.Item(0), Rectangle).Width)
End If
The Add Method
To add items to the list, use the Items.Add or Items.Insert method. The syntax of the Add
method is as follows:
ListBox1.Items.Add(item)
The item parameter is the object to be added to the list. You can add any object to the ListBox
control, but items are usually strings. The Add method appends new items to the end of the list,
unless the Sorted property has been set to True.
The following loop adds the elements of the array words to a ListBox control, one at a time:
Dim words(100) As String
{ statements to populate array }
Dim i As Integer
Fori=0To99
ListBox1.Items.Add(words(i))
Next
Similarly, you can iterate through all the items on the control by using a loop such as the
following:
Dim i As Integer
Fori=0To ListBox1.Items.Count - 1
{ statements to process item ListBox1.Items(i) }
Next
You can also use the For Each ... Next statement to iterate through the Items collection, as
shown here:
Dim itm As Object
For Each itm In ListBox1.Items
{ process the current item, represented by the itm variable }
NextPetroutsos c06.tex V3 - 01/28/2008 12:50pm Page 199
THE LISTBOX, CHECKEDLISTBOX, AND COMBOBOX CONTROLS 199
When you populate a ListBox control with a large number of items, call the BeginUpdate
method before starting the loop and call the EndUpdate method when you’re done. These
two methods turn off the visual update of the control while you’re populating it and they speed
up the process considerably. When the EndUpdate method is called, the control is redrawn with
all the items.
The Insert Method
To insert an item at a specific location, use the Insert method, whose syntax is as follows:
ListBox1.Items.Insert(index, item)
The item parameter is the object to be added, and index is the location of the new item. The first
item’s index in the list is zero. Note that you need not insert items at specific locations when the list
is sorted. If you do, the items will be inserted at the specified locations, but the list will no longer
be sorted.
The Clear Method
The Clear method removes all the items from the control. Its syntax is quite simple:
List1.Items.Clear
The Count Property
This is the number of items in the list. If you want to access all the items with a For ... Next loop,
the loop’s counter must go from 0 to ListBox.Items.Count - 1,asshownintheexampleofthe
Add method.
The CopyTo Method
The CopyTo method of the Items collection retrieves all the items from a ListBox control and stores
them in the array passed to the method as an argument. The syntax of the CopyTo method is
ListBox.CopyTo(destination, index)
where destination is the name of the array that will accept the items, and index is the index of
an element in the array where the first item will be stored. The array that will hold the items of the
control must be declared explicitly and must be large enough to hold all the items.
The Remove and RemoveAt Methods
To remove an itemfromthe list, you can simply call the Items collection’s Remove method, passing
the object to be removed as an argument. If the control contains strings, pass the string to be
removed. If the same string appears multiple times on the control, only the first instance will
be removed.
You can also remove an item by specifying its position in the list via the RemoveAt method,
which accepts as argument the position of the item to be removed:
ListBox1.Items.RemoveAt(index)
The index parameter is the order of the item to be removed, and the first item’s order is 0.Petroutsos c06.tex V3 - 01/28/2008 12:50pm Page 200
200 CHAPTER 6 BASIC WINDOWS CONTROLS
The Contains Method
The Contains method of the Items collection — not to be confused with the control’s Contains
method — accepts an object as an argument and returns a True/False value that indicates whether
the collection contains this object. Use the Contains method to avoid the insertion of identical
objects into the ListBox control. The following statements add a string to the Items collection, only
if the string isn’t already part of the collection:
Dim itm As String = ”Remote Computing”
If Not ListBox1.Items.Contains(itm) Then
ListBox1.Items.Add(itm)
End If
Selecting Items
The ListBox control allows the user to select either one or multiple items, depending on the setting
of the SelectionMode property. In a single-selection ListBox control, you can retrieve the selected
item by using the SelectedItem property, and its index by using the SelectedIndex property.
SelectedItem returns the selected item, which is an object. The text of the selected itemis reported
by the Text property.
If the control allows the selection of multiple items, they’re reported with the SelectedItems
property. This property is a collection of objects and exposes the same members as the Items
collection. Because the ComboBox does not allow the selection of multiple items, it provides only
the SelectedIndex and SelectedItem properties.
To iterate through all the selected items in a multiselection ListBox control, use a loop such as
the following:
Dim itm As Object
For Each itm In ListBox1.SelectedItems
Debug.WriteLine(itm)
Next
The itm variable should be declared as Object because the items in the ListBox control are
objects. If they’re all of the same type, you can convert them to the specific type and then call their
methods. If all the items are of the Rectangle type, you can use a loop like the following to print
the area of each rectangle:
Dim itm As Rectangle
For Each itm In ListBox1.SelectedItems
Debug.WriteLine(itm.Width * itm.Height)
Next
VB 2008 at Work: The ListBox Demo Project
The ListBox Demo application (shown in Figure 6.6) demonstrates the basic operations of the
ListBox control. The two ListBox controls on the form operate slightly differently. The first has
the default configuration: Only one item can be selected at a time, and new items are appended
after the existing item. The second ListBox control has its Sorted property set to True and its
MultiSelect property set according to the values of the two RadioButton controls at the bottom
of the form.Petroutsos c06.tex V3 - 01/28/2008 12:50pm Page 201
THE LISTBOX, CHECKEDLISTBOX, AND COMBOBOX CONTROLS 201
The code for the ListBox Demo application contains much of the logic you’ll need in your
ListBox manipulation routines. It shows you how to do the following:
◆ Add and remove items at runtime
◆ Transfer items between lists at runtime
◆ Handle multiple selected items
◆ Maintain sorted lists
Figure 6.6
ListBox Demo
demonstrates most of
the operations
you’ll perform with
ListBoxes.
The Add Item Buttons
The Add Item buttons use the InputBox() function to prompt the user for input, and then they
add the user-supplied string to the ListBox control. The code is identical for both buttons
(see Listing 6.10).
Listing 6.10: The Add New Element Buttons
Private Sub bttnSourceAdd Click(...)
Handles bttnSourceAdd.Click
Dim ListItem As String
ListItem = InputBox(”Enter new item’s name”)Petroutsos c06.tex V3 - 01/28/2008 12:50pm Page 202
202 CHAPTER 6 BASIC WINDOWS CONTROLS
If ListItem.Trim <> ”” Then
sourceList.Items.Add(ListItem)
End If
End Sub
Notice that the subroutine examines the data entered by the user to avoid adding blank strings
to the list. The code for the Clear buttons is also straightforward; it simply calls the Clear method
of the Items collection to remove all entries from the corresponding list.
Removing Items from the Two Lists
The code for the Remove Selected Item button is different from that for the Remove Selected Items
button (both are presented in Listing 6.11). The code for the Remove Selected Item button removes
the selected item, while the Remove Selected Items buttons must scan all the items of the left list
and remove the selected one(s).
Listing 6.11: The Remove Buttons
Private Sub bttnDestinationRemove Click(...)
Handles bttnDestinationRemove.Click
destinationList.Items.Remove( destinationList.SelectedItem)
End Sub
Private Sub bttnSourceRemove Click(...)
Handles bttnSourceRemove.Click
Dim i As Integer
For i = 0 To sourceList.SelectedIndices.Count - 1
sourceList.Items.RemoveAt( sourceList.SelectedIndices(0))
Next
End Sub
Even if it’s possible to remove an item by its value, this is not a safe approach. If two items have
the same name, the Remove method will remove the first one. Unless you’ve provided the code to
make sure that no identical items can be added to the list, remove them by their index, which
is unique.
Notice that the code always removes the first item in the SelectedIndices collection. If you
attempt to remove the item SelectedIndices(i), you will remove the first selected item, but
after that you will not remove all the selected items. After removing an item from the selection,
the remaining items are no longer at the same locations. (In effect, you have to refresh the
SelectedIndices collection.) The second selected itemwill take the place of the first selected item,
which was just deleted, and so on. By removing the first item in the SelectedIndices collection,
we make sure that all selected items, and only those items, will be eventually removed.
Moving Items between Lists
The two single-arrow buttons that are between the ListBox controls shown in Figure 6.6 transfer
selected items from one list to another. The button with the single arrow pointing to the rightPetroutsos c06.tex V3 - 01/28/2008 12:50pm Page 203
THE LISTBOX, CHECKEDLISTBOX, AND COMBOBOX CONTROLS 203
transfers the items selected in the left list, after it ensures that the list contains at least one selected
item. Its code is presented in Listing 6.12. First, it adds the item to the second list, and then it
removes the item from the original list. Notice that the code removes an item by passing it as an
argument to the Remove method because it doesn’t make any difference which one of two identical
objects will be removed.
Listing 6.12: Moving the Selected Items
Private Sub bttnSourceMove Click(...)
Handles bttnSourceMove.Click
While sourceList.SelectedIndices.Count > 0
destinationList.Items.Add(sourceList.Items(
sourceList.SelectedIndices(0)))
sourceList.Items.Remove(sourceList.Items(
sourceList.SelectedIndices(0)))
End While
End Sub
The second single-arrow button transfers items in the opposite direction. The destination
control (the one on the right) doesn’t allow the selection of multiple items, so you could use the
SelectedIndex and SelectedItem properties. Because the single selected element is also part of
the SelectedItems collection, you need not use a different approach. The statements that move a
single item from the right to the left ListBox are shown next:
sourceList.Items.Add(destinationList.SelectedItem)
destinationList.Items.RemoveAt(
destinationList.SelectedIndex)
Searching the ListBox
Two of the most useful methods of the ListBox control are the FindString and FindStringExact
methods, which allow you to quickly locate any item in the list. The FindString method locates
a string that partially matches the one you’re searching for; FindStringExact finds an exact
match. If you’re searching for Man, and the control contains a name such asMansfield, FindString
matches the item, but FindStringExact does not.
Both the FindString and FindStringExact methods perform case-insensitive searches.
If you’re searching for visual, and the list contains the item Visual, both methods will locate it.
Their syntax is the same:
itemIndex = ListBox1.FindString(searchStr As String)
where searchStr is the string you’re searching for. An alternative formof bothmethods allows
you to specify the order of the item at which the search will begin:
itemIndex = ListBox1.FindString(searchStr As String,
startIndex As Integer)Petroutsos c06.tex V3 - 01/28/2008 12:50pm Page 204
204 CHAPTER 6 BASIC WINDOWS CONTROLS
The startIndex argument allows you to specify the beginning of the search, but you can’t
specify where the search will end.
The FindString and FindStringExact methods work even if the ListBox control is not sorted.
You need not set the Sorted property to True before you call one of the searching methods on
the control. Sorting the list will help the search operation, but it takes the control less than 100
milliseconds to find an item in a list of 100,000 items, so time spent to sort the list isn’t worth it.
Before you load thousands of items in a ListBox control, however, you should probably consider
a more-functional interface.
VB 2008 at Work: The ListBoxFind Application
The application you’ll build in this section (seen in Figure 6.7) populates a list with a large
number of items and then locates any string you specify. Click the button Populate List to populate
the ListBox control with 10,000 random strings. This process will take a few seconds and will
populate the control with different random strings every time. Then, you can enter a string in
the TextBox control at the bottom of the form. As you type characters (or even delete characters
in the TextBox), the program will locate the closest match in the list and select (highlight) this item.
Figure 6.7
The ListBoxFind
application
The sample application reacts to each keystroke in the TextBox control and locates the string
you’re searching for instantly. The Find Item button does the same, but I thought I should
demonstrate the efficiency of the ListBox control and the type of functionality you’d expect in a
rich client application.
The code (shown in Listing 6.13) attempts to locate an exact match via the FindStringExact
method. If it succeeds, it reports the index of the matching element. If not, it attempts to locate
a near match with the FindString method. If it succeeds, it reports the index of the near match
(which is the first item on the control that partially matches the search argument) and terminates.
If it fails to find an exact match, it reports that the string wasn’t found in the list.Petroutsos c06.tex V3 - 01/28/2008 12:50pm Page 205
THE LISTBOX, CHECKEDLISTBOX, AND COMBOBOX CONTROLS 205
Listing 6.13: Searching the List
Private Sub TextBox1 TextChanged(...) Handles TextBox1.TextChanged
Dim srchWord As String = TextBox1.Text.Trim
If srchWord.Length = 0 Then Exit Sub
Dim wordIndex As Integer
wordIndex = ListBox1.FindStringExact(srchWord)
If wordIndex >= 0 Then
ListBox1.TopIndex = wordIndex
ListBox1.SelectedIndex = wordIndex
Else
wordIndex = ListBox1.FindString(srchWord)
If wordIndex >= 0 Then
ListBox1.TopIndex = wordIndex
ListBox1.SelectedIndex = wordIndex
Else
Debug.WriteLine(”Item ” & srchWord &
” is not in the list”)
End If
End If
End Sub
If you search for SAC, for example, and the control contains a string such as ”SAC” or ”sac” or
”sAc”, the program will return the index of the item in the list and will report an exact match. If
no exact match can be found, the program will return something like ”SACDEF”,ifsuchastring
exists on the control, as a near match. If none of the strings on the control starts with the characters
SAC, the search will fail.
Populating the List
The Populate List button creates 10,000 random items with the help of the Random class. First, it
generates a randomvalue in the range 1 through 20,which is the length of the string (not all strings
have the same length). Then the program generates as many random characters as the length of
the string and builds the string by appending each character to it. These random numbers are
in the range of 65 to 91 and they’re the ANSI values of the uppercase characters.
The ComboBox Control
The ComboBox control is similar to the ListBox control in the sense that it contains multiple items
and the user may select one, but it typically occupies less space onscreen. The ComboBox is
practically an expandable ListBox control, which can grow when the user wants to make a selec-
tion and retract after the selection is made. Normally, the ComboBox control displays one line
with the selected item, as this control doesn’t allow multiple item selection. The essential differ-
ence, however, between ComboBox and ListBox controls is that the ComboBox allows the user to
specify items that don’t exist in the list.Petroutsos c06.tex V3 - 01/28/2008 12:50pm Page 206
206 CHAPTER 6 BASIC WINDOWS CONTROLS
There are three types of ComboBox controls. The value of the control’s Style property
determines which box is used; these values are shown in Table 6.4.
Table 6.4: Styles of the ComboBox Control
Value Effect
DropDown (Default) The control is made up of a drop-down list, which is visible at all times, and a
text box. The user can select an item from the list or type a new one in the text box.
DropDownList This style is a drop-down list from which the user can select one of its items but can’t
enter a new one. The control displays a single item, and the list is expanded as needed.
Simple The control includes a text box and a list that doesn’t drop down. The user can select
from the list or type in the text box.
The ComboBox Styles project, shown in Figure 6.8, demonstrates the three styles of the
ComboBox control. This is another common element of the Windows interface, and its properties
and methods are identical to those of the ListBox control. Load the ComboBox Styles project in the
Visual Basic IDE and experiment with the three styles of the ComboBox control.
The DropDown and Simple ComboBox controls allow the user to select an item from the
list or enter a new one in the edit box of the control. Moreover, they’re collapsed by default
and they display a single item, unless the user expands the list of items to make a selection.
The DropDownList ComboBox is similar to a ListBox control in the sense that it restricts the user
to selecting an item (the user cannot enter a new one). However, it takes much less space on the
form than a ListBox does, because normally it displays a single item.When the user wants to make
a selection, the DropDownList expands to display more items. After the user has made a selection,
the list contracts to a single line again.
Figure 6.8a
The Simple ComboBox
displays a fixed number
of items at all times.Petroutsos c06.tex V3 - 01/28/2008 12:50pm Page 207
THE LISTBOX, CHECKEDLISTBOX, AND COMBOBOX CONTROLS 207
Figure 6.8b
The DropDown
ComboBox displays a
single item, and users
can either expand the
items or type something
in the edit box.
Figure 6.8c
The DropDownList
ComboBox expands to
display its items, but
doesn’t allow users to
type anything in the
edit box.Petroutsos c06.tex V3 - 01/28/2008 12:50pm Page 208
208 CHAPTER 6 BASIC WINDOWS CONTROLS
Most of the properties and methods of the ListBox control also apply to the ComboBox
control. The Items collection gives you access to the control’s items, and the SelectedIndices
and SelectedItems collections give you access to the items in the current selection. If the
control allows only a single item to be selected, use the properties SelectedIndex and
SelectedItem. You can also use the FindString and FindStringExact methods to locate
any item in the control.
There’s one aspect worth mentioning regarding the operation of the control. Although the edit
box at the top allows you to enter a new string, the new string doesn’t become a new item in the
list. It remains there until you select another item or you clear the edit box. You can provide some
code to make any string entered by the user in the control’s edit box be added to the list of
existing items.
The most common use of the ComboBox control is as a lookup table. The ComboBox control
takes up very little space on the form, but it can be expanded at will. You can save evenmore space
when the ComboBox is contracted by setting it to a width that’s too small for the longest item. Use
the DropDownWidth property, which is the width of the segment of the drop-down list. By default,
this property is equal to the control’s Width property. The second ComboBox control in Figure 6.8
contains an unusually long item. The control is wide enough to display the default selection.When
the user clicks the arrow to expand the control, the drop-down section of the control is wider
than the default width, so that the long items can be read.
Adding Items to a ComboBox at Runtime
Although the ComboBox control allows users to enter text in the control’s edit box, it doesn’t
provide a simple mechanism for adding new items at runtime. Let’s say you provide a ComboBox
with city names. Users can type the first few characters and quickly locate the desired item. But
what if you want to allow users to add new city names? You can provide this feature with two
simple techniques. The simpler one is to place a button with an ellipsis (three periods) right next
to the control. When users want to add a new item to the control, they can click the button and be
prompted for the new item.
A more-elegant approach is to examine the control’s Text property as soon as the control loses
focus, or the user presses the Enter key. If the string entered by the user doesn’t match an item on
the control, youmust add a new itemto the control’s Items collection and select the new itemfrom
within your code. The FlexComboBox project demonstrates how to use both techniques in your
code. The main form of the project, which is shown in Figure 6.9, is a simple data-entry screen. It’s
not the best data-entry form, but it’s meant for demonstration purposes.
Figure 6.9
The FlexComboBox
project demonstrates
two techniques for
adding new items to
aComboBoxatruntime.Petroutsos c06.tex V3 - 01/28/2008 12:50pm Page 209
THE LISTBOX, CHECKEDLISTBOX, AND COMBOBOX CONTROLS 209
You can either enter a city name (or country name) and press the Tab key to move to another
control or click the button next to the control to be prompted for a new city/country name. The
application will let you enter any city/country combination. You should provide code to limit
the cities within the selected country, but this is a nontrivial task. You also need to store the new
city names entered on the first ComboBox control to a file (or a database table), so users will find
them there the next time they execute the application. I haven’t made the application elaborate;
I’ve added the code only to demonstrate how to add new items to a ComboBox control at runtime.
VB 2008 At Work: The FlexCombo Project
The ellipsis button next to the City ComboBox control prompts the user for the new item via
the InputBox() function. Then it searches the Items collection of the control via the FindString
method, and if the new item isn’t found, it’s added to the control. Then the code selects the new
item in the list. To do so, it sets the control’s SelectedIndex property to the value returned by the
Items.Add method, or the value returned by the FindString method, depending on whether
the item was located or added to the list. Listing 6.14 shows the code behind the ellipsis button.
Listing 6.14: Adding a New Itemto the ComboBox Control at Runtime
Private Sub Button1 Click(...) Button1.Click
Dim itm As String
itm = InputBox(”Enter new item”, ”New Item”)
If itm.Trim <> ”” Then AddElement(itm)
End Sub
The AddElement() subroutine, which accepts a string as an argument and adds it to the control,
is shown in Listing 6.15. If the item doesn’t exist in the control, it’s added to the Items collection.
If the item is a member of the Items collection, it’s selected. As you will see, the same subroutine
will be used by the second method for adding items to the control at runtime.
Listing 6.15: The AddElement() Subroutine
Sub AddElement(ByVal newItem As String)
Dim idx As Integer
If ComboBox1.FindString(newItem) > 0 Then
idx = ComboBox1.FindString(newItem)
Else
idx = ComboBox1.Items.Add(newItem)
End If
ComboBox1.SelectedIndex = idx
End Sub
You can also add new items at runtime by adding the same code in the control’s LostFocus
event handler:
Private Sub ComboBox1 LostFocus(...) Handles ComboBox1.LostFocus
Dim newItem As String = ComboBox1.Text
AddElement(newItem)
End SubPetroutsos c06.tex V3 - 01/28/2008 12:50pm Page 210
210 CHAPTER 6 BASIC WINDOWS CONTROLS
The ScrollBar and TrackBar Controls
The ScrollBar and TrackBar controls let the user specify a magnitude by scrolling a selector
between its minimum and maximum values. In some situations, the user doesn’t know in advance
the exact value of the quantity to specify (in which case, a text box would suffice), so your
application must provide a more-flexible mechanism for specifying a value, along with some
type of visual feedback.
The vertical scroll bar that lets a user move up and down a long document is a typical example
of the use of the ScrollBar control. The scroll bar and visual feedback are the prime mechanisms for
repositioning the view in a long document or in a large picture thatwon’t fit entirely in its window.
The TrackBar control is similar to the ScrollBar control, but it doesn’t cover a continuous range
of values. The TrackBar control has a fixed number of tick marks, which the developer can label
(for example, Still, Slow, and Warp Speed, as shown in Figure 6.10). Users can place the slider’s
indicator to the desired value.Whereas the ScrollBar control relies on some visual feedback outside
the control to help the user position the indicator to the desired value, the TrackBar control
forces the user to select from a range of valid values.
In short, the ScrollBar control should be used when the exact value isn’t as important as the
value’s effect on another object or data element. The TrackBar control should be used when
the user can type a numeric value and the value your application expects is a number in a specific
range; for example, integers between 0 and 100, or a value between 0 and 5 inches in steps of 0.1
inches (0.0, 0.1, 0.2 ... 5.0). The TrackBar control is preferred to the TextBox control in similar
situations because there’s no need for data validation on your part. The user can specify only valid
numeric values with the mouse.
Figure 6.10
The TrackBar control
lets the user select one
of several discrete
values.
The ScrollBar Control
There’s no ScrollBar control per se in the Toolbox; instead, there are two versions of it: the HScroll-
Bar and VScrollBar controls. They differ only in their orientation, but because they share the same
members, I will refer to both controls collectively as ScrollBar controls. Actually, both controls
inherit from the ScrollBar control, which is an abstract control: It can be used to implement ver-
tical and horizontal scroll bars, but it can’t be used directly on a form. Moreover, the HScrollBar
and VScrollBar controls are not displayed in the Common Controls tab of the Toolbox. You have
to open the All Windows Forms tab to locate these two controls.
The ScrollBar control is a long stripe with an indicator that lets the user select a value between
the two ends of the control. The left (or bottom) end of the control corresponds to its minimum
value; the other end is the control’smaximumvalue. The current value of the control is determined
by the position of the indicator, which can be scrolled between the minimum and maximum
values. The basic properties of the ScrollBar control, therefore, are properly named Minimum,
Maximum,and Value.Petroutsos c06.tex V3 - 01/28/2008 12:50pm Page 211
THE SCROLLBAR AND TRACKBAR CONTROLS 211
Minimum The control’s minimum value. The default value is 0, but because this is an
Integer value, you can set it to negative values as well.
Maximum The control’s maximum value. The default value is 100, but you can set it to any
value that you can represent with the Integer data type.
Value The control’s current value, specified by the indicator’s position.
The Minimum and Maximum properties are Integer values. To cover a range of nonintegers, you
must supply the code to map the actual values to Integer values. For example, to cover a range
from 2.5 to 8.5, set the Minimum property to 25, set the Maximum property to 85, and divide the
control’s value by 10. If the range you need is from –2.5 to 8.5, do the same but set the Minimum
property to –25 and the Maximum value to 85, and divide the Value property by 10.
There are two more properties that allow you to control the movement of the indicator: the
SmallChange and LargeChange properties. The first property is the amount by which the indicator
changes when the user clicks one of the arrows at the two ends of the control. The LargeChange
property is the displacement of the indicator when the user clicks somewhere in the scroll bar
itself. You can manipulate a scroll bar by using the keyboard as well. Press the arrow keys to
move the indicator in the corresponding direction by SmallChange, and the PageUp/PageDown
keys to move the indicator by LargeChange.
VB 2008 at Work: The Colors Project
Figure 6.11 shows the main form of the Colors sample project, which lets the user specify a color
by manipulating the value of its basic colors (red, green, and blue) through scroll bars. Each basic
color is controlled by a scroll bar and has a minimum value of 0 and a maximum value of 255. If
you aren’t familiar with color definition in the Windows environment, see the section ‘‘Specifying
Colors’’ in Chapter 19, ‘‘Manipulating Images and Bitmaps.’’
Figure 6.11
The Colors application
demonstrates the use of
the ScrollBar control.
As the scroll bar is moved, the corresponding color is displayed, and the user can easily specify
a color without knowing the exact values of its primary components. All the user needs to know
is whether the desired color contains, for example, too much red or too little green. With the help
of the scroll bars and the immediate feedback from the application, the user can easily pinpoint
the desired color. Notice that the exact values of the color’s basic components are of no practical
interest; only the final color counts.Petroutsos c06.tex V3 - 01/28/2008 12:50pm Page 212
212 CHAPTER 6 BASIC WINDOWS CONTROLS
The ScrollBar Control’s Events
The user can change the ScrollBar control’s value in three ways: by clicking the two arrows at its
ends, by clicking the area between the indicator and the arrows, and by dragging the indicator
with the mouse. You can monitor the changes of the ScrollBar’s value from within your code by
using two events: ValueChanged and Scroll. Both events are fired every time the indicator’s
position is changed. If you change the control’s value from within your code, only the
ValueChanged event will be fired.
The Scroll event can be fired in response to many different actions, such as the scrolling of the
indicator with the mouse, a click on one of the two buttons at the ends of the scroll bars, and so
on. If you want to know the action that caused this event, you can examine the Type property of
the second argument of the event handler. The settings of the e.Type property are members of the
ScrollEventType enumeration (LargeDecrement, SmallIncrement, Track,andsoon).
Handling the Events in the Colors Application
The Colors application demonstrates how to program the two events of the ScrollBar control. The
two PictureBox controls display the color designed with the three scroll bars. The left PictureBox
is colored from within the Scroll event, whereas the other one is colored from within the
ValueChanged event. Both events are fired as the user scrolls the scrollbar’s indicator, but in
the Scroll event handler of the three scroll bars, the code examines the value of the e.Type
property and reacts to it only if the event was fired because the scrolling of the indicator has ended.
For all other actions, the event handler doesn’t update the color of the left PictureBox.
If the user attempts to change the Color value by clicking the two arrows of the scroll bars or by
clicking in the area to the left or to the right of the indicator, both PictureBox controls are updated.
While the user slides the indicator or keeps pressing one of the end arrows, only the PictureBox to
the right is updated.
The conclusion from this experiment is that you can program either event to provide
continuous feedback to the user. If this feedback requires too many calculations, which would
slow down the reaction of the corresponding event handler, you can postpone the reaction until
the user has stopped scrolling the indicator. You can detect this condition by examining the value
of the e.Type property.When it’s ScrollEventType.EndScroll, you can execute the appropriate
statements. Listing 6.16 shows the code behind the Scroll and ValueChanged events of the scroll
bar that controls the red component of the color. The code of the corresponding events of the other
two controls is identical.
Listing 6.16: Programming the ScrollBar Control’s Scroll Event
Private Sub redBar Scroll(...) Handles redBar.Scroll
If e.Type = ScrollEventType.EndScroll Then
ColorBox1()
lblRed.Text = ”RED ” & redBar.Value.ToString(”###”)
End If
End Sub
Private Sub redBar ValueChanged(...) Handles redBar.ValueChanged
ColorBox2()
End SubPetroutsos c06.tex V3 - 01/28/2008 12:50pm Page 213
THE SCROLLBAR AND TRACKBAR CONTROLS 213
The ColorBox1() and ColorBox2() subroutines update the color of the two PictureBox
controls by setting their background colors. You can open the Colors project in Visual Studio and
examine the code of these two routines.
The TrackBar Control
The TrackBar control is similar to the ScrollBar control, but it lacks the granularity of ScrollBar.
Suppose that you want the user of an application to supply a value in a specific range, such as the
speed of a moving object. Moreover, you don’t want to allow extreme precision; you need only
a few settings, as shown in the examples of Figures 6.10 and 6.12. The user can set the control’s
value by sliding the indicator or by clicking on either side of the indicator.
Figure 6.12
The Inches application
demonstrates the use
of the TrackBar control
in specifying an exact
value in a specific range.
Granularity is how specific youwant to be inmeasuring. Inmeasuring distances between towns,
a granularity of amile is quite adequate. In measuring (or specifying) the dimensions of a building,
the granularity could be on the order of a foot or an inch. The TrackBar control lets you set the type
of granularity that’s necessary for your application.
Similar to the ScrollBar control, SmallChange and LargeChange properties are available.
SmallChange is the smallest increment by which the Slider value can change. The user can change
the slider by the SmallChange value only by sliding the indicator. (Unlike the ScrollBar
control, there are no arrows at the two ends of the Slider control.) To change the Slider’s value by
LargeChange, the user can click on either side of the indicator.
VB 2008 at Work: The Inches Project
Figure 6.12 demonstrates a typical use of the TrackBar control. The form in the figure is an
element of a program’s user interface that lets the user specify a distance between 0 and 10 inches
in increments of 0.2 inches. As the user slides the indicator, the current value is displayed on a
Label control below the TrackBar. If you open the Inches application, you’ll notice that there are
more stops than there are tick marks on the control. This is made possible with the TickFrequency
property, which determines the frequency of the visible tick marks.
You might specify that the control has 50 stops (divisions), but that only 10 of them will be
visible. The user can, however, position the indicator on any of the 40 invisible tick marks. You can
think of the visible marks as the major tick marks, and the invisible ones as the minor tick marks.
If the TickFrequency property is 5, only every fifth mark will be visible. The slider’s indicator,
however, will stop at all tick marks.
When using the TrackBar control on your interfaces, you should set the TickFrequency
property to a value that helps the user select the desired setting. Too many tick marks are
confusing and difficult to read. Without tick marks, the control isn’t of much help. You might also
consider placing a few labels to indicate the value of selected tick marks, as I have done in
this example.Petroutsos c06.tex V3 - 01/28/2008 12:50pm Page 214
214 CHAPTER 6 BASIC WINDOWS CONTROLS
The properties of the TrackBar control in the Inches application are as follows:
Minimum =0
Maximum =50
SmallChange =1
LargeChange =5
TickFrequency =5
The TrackBar needs to cover a range of 10 inches in increments of 0.2 inches. If you set the
SmallChange property to 1, you have to set LargeChange to 5. Moreover, the TickFrequency is
set to 5, so there will be a total of five divisions in every inch. The numbers below the tick marks
were placed there with properly aligned Label controls.
The label at the bottomneeds to be updated as the TrackBar’s value changes. This is signaled to
the application with the Change event, which occurs every time the value of the control changes,
either through scrolling or from within your code. The ValueChanged event handler of the
TrackBar control is shown next:
Private Sub TrackBar1 ValueChanged(...)
Handles TrackBar1.ValueChanged
lblInches.Text = ”Length in inches = ” &
Format(TrackBar1.Value / 5, ”#.00”)
End Sub
The Label controls below the tick marks can also be used to set the value of the control. Every
time you click one of the labels, the following statement sets the TrackBar control’s value. Notice
that all the Label controls’ Click events are handled by a common handler:
Private Sub Label Click(...)
Handles Label1.Click, Label9.Click
TrackBar1.Value = sender.text * 5
End Sub
The BottomLine
Use the TextBox control as a data-entry and text-editing tool. The TextBox control is the
most common element of the Windows interface, short of the Button control, and it’s used
to display and edit text. You can use a TextBox control to prompt users for a single line of
text (such as a product name) or a small document (a product’s detailed description).
Master It What are the most important properties of the TextBox control? Which ones
would you set in the Properties windows at design-time?
Master It How will you implement a control that suggests lists of words matching the
characters entered by the user?
Use the ListBox, CheckedListBox, and ComboBox controls to present lists of items. The
ListBox control contains a list of items from which the user can select one or more, depending
on the setting of the SelectionMode property.
Master It How will you locate an item in a ListBox control?Petroutsos c06.tex V3 - 01/28/2008 12:50pm Page 215
THE BOTTOM LINE 215
Use the ScrollBar and TrackBar controls to enable users to specify sizes and positions with
the mouse. The ScrollBar and TrackBar controls let the user specify a magnitude by scrolling
a selector between its minimum and maximum values. The ScrollBar control uses some visual
feedback to display the effects of scrolling on another entity, such as the current view in a long
document.
Master It Which event of the ScrollBar control would you code to provide visual feedback to
the user?Petroutsos c06.tex V3 - 01/28/2008 12:50pm Page 216Petroutsos c07.tex V3 - 01/28/2008 1:11pm Page 217
Chapter 7
Working with Forms
In Visual Basic, the form is the container for all the controls that make up the user interface. When
a Visual Basic application is executing, each window it displays on the desktop is a form. The
terms form and window describe the same entity. A window is what the user sees on the desktop
when the application is running. A form is the same entity at design time. The proper term is a
Windows form, as opposed to a web form,butIwillrefertothemas forms. This term includes both
the regular forms and dialog boxes, which are simple forms you use for very specific actions, such
as to prompt the user for a particular piece of data or to display critical information. A dialog box
is a form with a small number of controls, no menus, and usually an OK and a Cancel button to
close it.
Forms have a built-in functionality that is always available without any programming effort on
your part. You can move a form around, resize it, and even cover it with other forms. You do so
with the mouse or with the keyboard through the Control menu.
In previous chapters, you concentrated on placing the elements of the user interface on forms,
setting their properties, and adding code behind selected events. Now, you’ll look at forms
themselves and at a few related topics. In this chapter, you’ll learn how to do the following:
◆ Use forms’ properties
◆ Design applications with multiple forms
◆ Design dynamic forms
◆ Design menus
Forms have many trivial properties that won’t be discussed here. Instead, let’s jump directly to
the properties that are unique to forms and then look at how to manipulate forms from within an
application’s code.
TheAppearance of Forms
Applications are made up of one or more forms — usually more than one. You should craft your
forms carefully, make them functional, and keep them simple and intuitive. You already know
how to place controls on the form, but there’s more to designing forms than populating them with
controls. The main characteristic of a form is the title bar on which the form’s caption is displayed
(see Figure 7.1).
Clicking the icon on the left end of the title bar opens the Control menu, which contains the
commands shown in Table 7.1. On the right end of the title bar are three buttons: Minimize,
Maximize, and Close. Clicking these buttons performs the associated function. When a form isPetroutsos c07.tex V3 - 01/28/2008 1:11pm Page 218
218 CHAPTER 7 WORKING WITH FORMS
maximized, the Maximize button is replaced by the Restore button. When clicked, the Restore
button resets the form to the size and position before it was maximized, and it’s replaced by the
Maximize button. To access the Control menu without a mouse, press Alt and then the down
arrow key.
Figure 7.1
The elements of the
form
Table 7.1: Commands of the Control Menu
Command Effect
Restore Restores a maximized form to the size it was before it was maximized; available only if the
form has been maximized.
Move Lets the user move the form around with the arrow keys.
Size Lets the user resize the form with the arrow keys.
Minimize Minimizes the form.
Maximize Maximizes the form.
Close Closes the current form. (Closing the application’s main form terminates the application.)
Properties of the Form Object
You’re familiar with the appearance of forms, even if you haven’t programmed in the Windows
environment in the past; you have seen nearly all types of windows in the applications you’rePetroutsos c07.tex V3 - 01/28/2008 1:11pm Page 219
THE APPEARANCE OF FORMS 219
using every day. The floating toolbars used by many graphics applications, for example, are actu-
ally forms with a narrow title bar. The dialog boxes that display critical information or prompt
you to select the file to be opened are also forms. You can duplicate the look of any window or
dialog box through the following properties of the Form object.
AcceptButton, CancelButton
These two properties let you specify the default Accept and Cancel buttons. The Accept button
is the one that’s automatically activated when you press Enter, no matter which control has the
focus at the time, and is usually the button with the OK caption. Likewise, the Cancel button is
the one that’s automatically activated when you hit the Esc key and is usually the button with the
Cancel caption. To specify the Accept and Cancel buttons on a form, locate the AcceptButton and
CancelButton properties of the form and select the corresponding controls from a drop-down
list, which contains the names of all the buttons on the form. For more information on these two
properties, see the section ‘‘Forms versus Dialog Boxes,’’ later in this chapter.
AutoScaleMode
This property determines how the control is scaled, and its value is a member of the AutoScale-
Mode enumeration: None (automatic scaling is disabled), Font (the controls on the form are scaled
relative to the size of their font), Dpi, which stands for dots per inch (the controls on the form are
scaled relative to the display resolution), and Inherit (the controls are scaled according to the
AutoScaleMode property of their parent class). The default value is Font; if you change the form’s
font size, the controls on it are scaled to the new font size.
AutoScroll
The AutoScroll property is a True/False value that indicates whether scroll bars will be auto-
matically attached to the form (as seen in Figure 7.2) if the form is resized to a point that not all
itscontrolsarevisible.Usethispropertytodesign large forms without having to worry about
the resolution of the monitor on which they’ll be displayed. The AutoScroll property is used in
conjunction with two other properties (described a little later in this section): AutoScrollMargin
and AutoScrollMinSize. Note that the AutoScroll property applies to a few controls as well,
including the Panel and SplitContainer controls. For example, you can create a form with a fixed
and a scrolling pane by placing two Panel controls on it and setting the AutoScroll property of
one of them (the Panel you want to scroll) to True.
AutoScrollPosition
This property is available from within your code only (you can’t set this property at design time),
and it indicates the number of pixels that the form was scrolled up or down. Its initial value is
zero, and it assumes a value when the user scrolls the form (provided that the form’s AutoScroll
property is True). Use this property to find out the visible controls from within your code, or scroll
the form programmatically to bring a specific control into view.
AutoScrollMargin
This is a margin, expressed in pixels, that’s added around all the controls on the form. If the form
is smaller than the rectangle that encloses all the controls adjusted by the margin, the appropriate
scroll bar(s) will be displayed automatically.Petroutsos c07.tex V3 - 01/28/2008 1:11pm Page 220
220 CHAPTER 7 WORKING WITH FORMS
Figure 7.2
If the controls don’t fit
in the form’s visible
area, scroll bars can be
attached automatically.
AutoScrollMinSize
This property lets you specify the minimum size of the form before the scroll bars are attached.
If your form contains graphics that you want to be visible at all times, set the Width and Height
members of the AutoScrollMinSize property to the dimensions of the graphics. (Of course, the
graphics won’t be visible at all times, but the scroll bars indicate that there’s more to the form than
can fit in the current window.) Notice that this isn’t the form’s minimum size; users can make
the form even smaller. To specify a minimum size for the form, use the MinimumSize property,
described later in this section.
Let’s say the AutoScrollMargin property of the form is 180 × 150. If the form is resized to
fewer than 180 pixels horizontally or 150 pixels vertically, the appropriate scroll bars will appear
automatically, as long as the AutoScroll property is True. If you want to enable the Auto-
Scroll feature when the form’s width is reduced to anything fewer than 250 pixels, set the
AutoScrollMinSize property to (250, 0). In this example, setting AutoScrollMinSize.Width
to anything less than 180, or AutoScrollMinSize.Height to anything less than 150, will have no
effect on the appearance of the form and its scroll bars.
Bringing Selected Controls into View
In addition to the Autoscroll properties, the Form object provides the Scroll method, which
allows you to scroll a form programmatically, and ScrollControlIntoView, which scrolls the form
until the specified control comes into view. The Scroll method accepts as arguments the horizontal
and vertical displacements of the scrolling operation, whereas ScrollControlIntoView accepts as
an argument the control you want to bring into view. Notice that activating a control with the Tab key
automatically brings the control into view if it’s not already visible on the form. Finally, the Scroll
event is fired every time a form is scrolled.
FormBorderStyle
The FormBorderStyle property determines the style of the form’s border; its value is one
of the FormBorderStyle enumeration’s members, which are shown in Table 7.2. You can make
the form’s title bar disappear altogether by setting the form’s FormBorderStyle property toPetroutsos c07.tex V3 - 01/28/2008 1:11pm Page 221
THE APPEARANCE OF FORMS 221
FixedToolWindow,the ControlBox property to False, and the Text property (the form’s caption)
to an empty string. However, a form like this can’t be moved around with the mouse and will
probably frustrate users.
Table 7.2: The FormBorderStyle Enumeration
Value Effect
None A borderless window that can’t be resized. This setting is rarely used.
Sizable (default) A resizable window that’s used for displaying regular forms.
Fixed3D A window with a fixed visible border, ‘‘raised’’ relative to the main area. Unlike the
None setting, this setting allows users to minimize and close the window.
FixedDialog A fixed window used to implement dialog boxes.
FixedSingle A fixed window with a single-line border.
FixedToolWindow A fixed window with a Close button only. It looks like a toolbar displayed by
drawing and imaging applications.
SizableToolWindow Same as the FixedToolWindow, but is resizable. In addition, its caption font is
smaller than the usual.
ControlBox
This property is also True by default. Set it to False to hide the control box icon and disable the
Control menu. Although the Control menu is rarely used, Windows applications don’t disable it.
When the ControlBox property is False, the three buttons on the title bar are also disabled. If you
set the Text property to an empty string, the title bar disappears altogether.
MinimizeBox, MaximizeBox
These two properties, which specify whether the Minimize and Maximize buttons will appear on
the form’s title bar, are True by default. Set them to False to hide the corresponding buttons on the
form’s title bar.
MinimumSize, MaximumSize
These two properties read or set the minimum and maximum size of a form. When users resize
the form at runtime, the form won’t become any smaller than the dimensions specified by the
MinimumSize property and no larger than the dimensions specified by the MaximumSize property.
The MinimumSize property is a Size object, and you can set it with a statement like the following:
Me.MinimumSize = New Size(400, 300)
Or you can set the width and height separately:
Me.MinimumSize.Width = 400
Me.MinimumSize.Height = 300Petroutsos c07.tex V3 - 01/28/2008 1:11pm Page 222
222 CHAPTER 7 WORKING WITH FORMS
The MinimumSize.Height property includes the height of the form’s title bar; you should take
that into consideration. If the minimum usable size of the form is 400 × 300, use the following
statement to set the MinimumSize property:
Me.MinimumSize = New Size(400, 300 + SystemInformation.CaptionHeight)
The default value of both properties is (0, 0), which means that no minimum or maximum size
is imposed on the form, and the user can resize it as desired.
Use the SystemInformation Class to Read System Information
The height of the caption is not a property of the Form object, even though it’s used to determine
the useful area of the form (the total height minus the caption bar). Keep in mind that the height
of the caption bar is given by the CaptionHeight property of the SystemInformation object. You
should look up the SystemInformation object, which exposes a lot of useful properties — such as
BorderSize (the size of the form’s borders), Border3DSize (the size of three-dimensional borders),
CursorSize (the cursor’s size), and many more.
KeyPreview
This property enables the form to capture all keystrokes before they’re passed to the control that
has the focus. Normally, when you press a key, the KeyPress event of the control with the focus
is triggered (as well as the KeyUp and KeyDown events), and you can handle the keystroke from
within the control’s appropriate handler. In most cases, you let the control handle the keystroke
and don’t write any form code for that.
If you want to use ‘‘universal’’ keystrokes in your application, you must set the KeyPreview
property to True. Doing so enables the form to intercept all keystrokes, so you can process them
fromwithin the form’s keystroke event handlers. To handle a specific keystroke at the form’s level,
set the form’s KeyPreview property to True and insert the appropriate code in the form’s KeyDown
or KeyUp event handler (the KeyPress event isn’t fired for the function keys).
The same keystrokes are then passed to the control with the focus, unless you ‘‘kill’’ the
keystroke by setting its SuppressKeystroke property to True when you process it on the form’s
level. Formore information on processing keystrokes at the formlevel and using special keystrokes
throughout your application, see the Contacts project later in this chapter.
SizeGripStyle
This property gets or sets the style of the sizing handle to display in the bottom-right corner of
the form. You can set it to a member of the SizeGripStyle enumeration: Auto (the size grip is
displayed as needed), Show (the size grip is displayed at all times), or Hide (the size grip is not
displayed, but users can still resize the form with the mouse).
StartPosition, Location
The StartPosition property, which determines the initial position of the form when it’s first
displayed, can be set to one of the members of the FormStartPosition enumeration: Center-
Parent (the form is centered in the area of its parent form), CenterScreen (the form is centered
on the monitor), Manual (the position of the form is determined by the Location property),
WindowsDefaultLocation (the form is positioned at the Windows default location), andPetroutsos c07.tex V3 - 01/28/2008 1:11pm Page 223
THE APPEARANCE OF FORMS 223
WindowsDefaultBound (the form’s location and bounds are determined by Windows defaults).
The Location property allows you to set the form’s initial position at design time or to change the
form’s location at runtime.
TopMost
This property is a True/False value that lets you specify whether the form will remain on top of all
other forms in your application. Its default property is False, and you should change it only on rare
occasions. Some dialog boxes, such as the Find & Replace dialog box of any text-processing appli-
cation, are always visible, even when they don’t have the focus. For more information on using
the TopMost property, see the discussion of the TextPad project in Chapter 6, ‘‘Basic Windows
Controls.’’ You can also add a professional touch to your application by providing a CheckBox
control that determines whether a form should remain on top of all other forms of the application.
Size
Use the Size property to set the form’s size at design time or at runtime. Normally, the form’s
width and height are controlled by the user at runtime. This property is usually set from within
the form’s Resize event handler to maintain a reasonable aspect ratio when the user resizes the
form. The Form object also exposes the Width and Height properties for controlling its size.
Placing Controls on Forms
The first step in designing your application’s interface is, of course, the analysis and careful
planning of the basic operations you want to provide through your interface. The second step is
to design the forms. Designing a form means placing Windows controls on it, setting the controls’
properties, and then writing code to handle the events of interest. Visual Studio 2008 is a rapid
application development (RAD) environment. This doesn’t mean that you’re expected to develop
applications rapidly. It has come to mean that you can rapidly prototype an application and show
something to the customer. And this is made possible through the visual tools that come with VS
2008, especially the new Form Designer.
To place controls on your form, you select them in the Toolbox and then draw, on the form,
the rectangle in which the control will be enclosed. Or you can double-click the control’s icon to
place an instance of the control on the form. All controls have a default size, and you can resize
the control on the form by using the mouse.
Each control’s dimensions can also be set in the Properties window through the Size property.
The Size property exposes the Width and Height components, which are expressed in pixels.
Likewise, the Location property returns (or sets) the coordinates of the top-left corner of the
control. In the section ‘‘Building Dynamic Forms at Runtime,’’ later in this chapter, you’ll see how
to create new controls at runtime and place them in a specific location on a form from within
your code.
As you place controls on the form, you can align them in groups by using the commands of the
Format menu. Select multiple controls on the form by using the mouse and the Shift (or Ctrl) key,
and then align their edges or their middles with the appropriate command of the Format menu.
To align the left edges of a column of TextBoxes, choose the Format  Align  Left command. You
can also use the commands of the Format  Make Same Size command to adjust the dimensions
of the selected controls. (To make them equal in size, make their widths or heights equal.)
As you move controls around with the mouse, a blue snap line appears when the controls
become nearly aligned with another control. Release the mouse while the snap line is visible to
leave the control aligned with the one indicated by the snap lines. The blue snap lines indicate edge
alignment. Most of the time, we need to align not the edges of two controls, but their baselinesPetroutsos c07.tex V3 - 01/28/2008 1:11pm Page 224
224 CHAPTER 7 WORKING WITH FORMS
(the baseline of the text on the control). The snap lines that indicate baseline alignment are red.
Figure 7.3 shows both types of snap lines. When we’re aligning a Label control with its matching
TextBox control on a form, we want to align their baselines, not their frames (especially if you con-
sider that the Label controls are always displayed without borders). If the control is aligned with
other controls in both directions, two snap lines will appear— a horizontal one and a vertical one.
Figure 7.3
Edge alignment (top)
and baseline alignment
(bottom)
Setting the TabOrder Property
Another important issue in form design is the tab order of the controls on the form. As you know,
pressing the Tab key at runtime takes you to the next control on the form. The order of the controls
is the order in which they were placed on the form, but this is never what we want. When you
design the application, you can specify in which order the controls receive the focus (the tab order,
as it is known) with the help of the TabOrder property. Each control has its own TabOrder setting,
which is an integer value. When the Tab key is pressed, the focus is moved to the control whosePetroutsos c07.tex V3 - 01/28/2008 1:11pm Page 225
THE APPEARANCE OF FORMS 225
tab order immediately follows the tab order of the current control. The values of the TabOrder
properties of the various controls on the form need not be consecutive.
To specify the tab order of the various controls, you can set their TabOrder property in the
Properties window or you can choose the Tab Order command from the View menu. The tab
order of each control will be displayed on the corresponding control, as shown in Figure 7.4. (The
form shown in the figure is the Contacts application, which is discussed shortly.)
Figure 7.4
Setting the tab order of
the controls on the main
form of the Contacts
project
To set the tab order of the controls, click each control in the order in which you want them to
receive the focus. You must click all of them in the desired order, starting with the first control in
the tab order. Each control’s index in the tab order appears in the upper-left corner of the control.
When you’re finished, choose the Tab Order command from the View menu again to hide these
numbers.
As you place controls on the form, don’t forget to lock them, so that you won’t move them
around by mistake as you work with other controls. You can lock the controls in their places either
by setting each control’s Locked property to True or by locking all the controls on the form at once
via the Format  Lock Controls command.
Design with the User In Mind
Designing functional forms is a crucial step in the process of developing Windows applications. Most
data-entry operators don’t work with the mouse, and you mustmake sure that all the actions (such as
switching to another control, opening a menu, clicking a button, and so on) can be performed with
the keyboard. This requirement doesn’t apply to graphics applications, of course, but most applica-
tions developed with VB are business applications, and users should be able to perform most of the
tasks with the keyboard, not with the mouse.Petroutsos c07.tex V3 - 01/28/2008 1:11pm Page 226
226 CHAPTER 7 WORKING WITH FORMS
In my experience, the most important aspect of the user interface of a business application is the
handling of the Enter keystroke. When a TextBox control has the focus, the Enter keystroke should
advance the focus to the next control in the tab order; when a list control (such as the ListBox or
ListView control) has the focus, the Enter keystroke should invoke the same action as double-clicking
the current item. The sample project in the following section demonstrates many of the features
you’d expect from a data-entry application.
If you’re developing a data-entry form, you must take into consideration the needs of the users. Make
a prototype and ask the people who will use the application to test-drive it. Listen to their objections
carefully, collect all the information, and then use it to refine your application’s user interface. Don’t
defend your design — just learn from the users. They will uncover all the flaws of the application and
they’ll help you design the most functional interface. In addition, they will accept the finished appli-
cation with fewer objections and complaints if they know what to expect.
VB 2008 at Work: The Contacts Project
I want to conclude this section with a simple data-entry application that demonstrates many of
the topics discussed here, as well as a few techniques for designing easy-to-use forms. Figure 7.5
shows a data-entry form for maintaining contact information, and I’m sure you will add your own
fields to make this application more useful.
Figure 7.5
A simple data-entry
screen
You can navigate through the contacts by clicking the buttons with the arrows, as well as add
new contacts or delete existing ones by clicking the appropriate buttons. When you’re entering a
new contact, the buttons shown in Figure 7.5 are replaced by the usual OK and Cancel buttons.
The action of adding a new contact, or editing an existing one, must end by clicking one of these
two buttons. After committing a new contact or canceling the action, the usual navigation buttons
appear again.Petroutsos c07.tex V3 - 01/28/2008 1:11pm Page 227
THE APPEARANCE OF FORMS 227
Place the controls you see in Figure 7.5 on the form and align them appropriately. After the
controls are on the form, the next step is to set their tab order. You must specify a TabOrder
even for controls that never receive focus, such as the Label controls. In addition to the tab order
of the controls, we’ll also use shortcut keys to give the user quick access to the most common
fields. The shortcut keys are displayed as underlined characters on the corresponding labels.
Notice that the Label controls have shortcut keys, even though they don’t receive the focus. When
you press the shortcut key of a Label, the focus is moved to the following control in the tab order,
which is the TextBox control next to it.
If you run the application now, you’ll see that the focus moves from one TextBox to the next
and that the labels are skipped. After the last TextBox control, the focus is moved to the buttons
and then back to the first TextBox control. To add a shortcut key for the most common fields,
determine which fields will have shortcut keys and then which keys will be used for that purpose.
Being the Internet buffs that we all are, let’s assign shortcut keys to the Company, EMail, and URL
fields. Locate each label’s Text property in the Properties window and insert the & symbol in
front of the character you want to act as a shortcut for each Label. The Text properties of the three
controls should be &Company, &EMail,and &URL.
Shortcut keys are activated at runtime by pressing the shortcut character while holding down
the Alt key. The shortcut key will move the focus to the corresponding Label control, but because
labels can’t receive the focus, the focus is moved immediately to the next control in the tab order,
which is the adjacent TextBox control.
The contacts are stored in an ArrayList object, which is similar to an array but a little more
convenient. We’ll discuss ArrayLists in Chapter 14, ‘‘Storing Data in Collections’’; for now, you
can ignore the parts of the application that manipulate the contacts and focus on the design issues.
Start by loading the sample data included with the application. Open the File menu and choose
Load. You won’t be prompted for a filename; the application always opens the same file in its root
folder. After reading about the OpenFileDialog and SaveFileDialog controls, you can modify the
code so that it prompts the user about the file to read from or write to. Then enter a new contact by
clicking the Add button or edit an existing contact by clicking the Edit button. Both actions must
end with the OK or Cancel button. In other words, we require users to explicitly end the operation,
and we won’t allow them to switch to another contact while adding or editing one.
The code behind the various buttons is straightforward. The Add button hides all the nav-
igational buttons at the bottom of the form and clears the TextBoxes. The OK button saves the
new contact to an ArrayList structure and redisplays the navigational buttons. The Cancel button
ignores the data entered by the user and likewise displays the navigational buttons. In all cases,
when the user switches back to the view mode, the TextBoxes are also locked, by setting their
ReadOnly properties to True.
Handling Keystrokes
Although the Tab key is the Windows method of moving to the next control on the form, most
users will find it more convenient to use the Enter key. The Enter key is the most important one
on the keyboard, and applications should handle it intelligently. When the user presses Enter in a
single-line TextBox, for example, the obvious action is to move the focus to the following control.
I included a few statements in the KeyDown event handlers of the TextBox controls to move the
focus to the following one:
Private Sub txtAddress1 KeyDown(...) Handles txtAddress1.KeyDown
If e.KeyData = Keys.Enter ThenPetroutsos c07.tex V3 - 01/28/2008 1:11pm Page 228
228 CHAPTER 7 WORKING WITH FORMS
e.SuppressKeyPress = True
txtAddress2.Focus()
End If
End Sub
If you use the KeyUp event handler instead, the result won’t be any different, but an annoying
beeping sound will be emitted with each keystroke. The beep occurs when the button is depressed,
so we must intercept the Enter key as soon as it happens, and not after the control receives the
notification for the KeyDown event. The control will still catch the KeyUp event and it will beep
because it’s a single-line TextBox control (an audible warning that the specific key shouldn’t be
used in a single-line TextBox control). To avoid the beep sound, the code ‘‘kills’’ the keystroke by
setting the SuppressKeystroke property to True.
Processing Keys from within Your Code
The code shown in the preceding KeyDown event handler will work, but you must repeat it for every
TextBox control on the form. A more convenient approach is to capture the Enter keystroke in the
form’s KeyDown event handler and process it for all TextBox controls. First, we must figure out
whether the control with the focus is a TextBox control. The property Me.ActiveControl returns a
reference to the control with the focus. To find out the type of the active control and compare it to
the TextBox control’s type, use the following If statement:
If Me.ActiveControl.GetType Is GetType(TextBox) Then
’ process the Enter key
End If
An interesting method of the Form object is the ProcessTabKey method, which imitates the Tab
keystroke. Calling the ProcessTabKey method is equivalent to pressing the Tab key fromwithin your
code. The method accepts a True/False value as an argument, which indicates whether it will move
the focus to the next control in the tab order (if True), or to the previous control in the tab order. Once
you can figure out the active control’s type and you have a method of simulating the Tab keystroke
from within your code, you don’t have to code every TextBox control’s KeyDown event.
Start by setting the form’s KeyPreview property to True and then insert the following statements in
the form’s KeyDown event handler:
If e.KeyCode = Keys.Enter Then
If Me.ActiveControl.GetType Is GetType(TextBox) Then
e.SuppressKeyPress = True
If e.Shift Then
Me.ProcessTabKey(False)
Else
Me.ProcessTabKey(True)
End If
End If
End IfPetroutsos c07.tex V3 - 01/28/2008 1:11pm Page 229
THE APPEARANCE OF FORMS 229
The last topic demonstrated in this example is how to capture certain keystrokes, regardless of
the control that has the focus. We’ll use the F10 keystroke to display the total number of contacts
entered so far. Assuming that you have already set the form’s KeyPreview property to True, enter
the following code in the form’s KeyDown event:
If e.Keycode = keys.F10 Then
MsgBox(”There are ” & Contacts.Count.ToString &
” contacts in the database”)
e.Handled = True
End If
Listing 7.1 shows the complete handler for the form’s KeyDown event, which also allows you to
move to the next or previous contact by using the Alt+Plus or Alt+Minus keys, respectively.
Listing 7.1: Handling Keystrokes in the Form’s KeyDown Event Handler
Public Sub Form1 KeyDown(ByVal sender As Object,
ByVal e As System.WinForms.KeyEventArgs)
Handles Form1.KeyUp
If e.Keycode = Keys.F10 Then
MsgBox(”There are ” & Contacts.Count.ToString &
” contacts in the database”)
e.Handled = True
End If
If e.KeyCode = Keys.Subtract And e.Modifiers = Keys.Alt Then
bttnPrevious.PerformClick
End If
If e.KeyCode = Keys.Add And e.Modifiers = Keys.Alt Then
bttnNext.PerformClick
End If
End Sub
The KeyCode property of the e argument returns the code of the key that was pressed. All key
codes are members of the Keys enumeration, so you need not memorize them. The name of the
key with the plus symbol is Keys.Add.The Modifiers property of the same argument returns the
modifier key(s) that were held down while the key was pressed. Also, all possible values of the
Modifiers property are members of the Keys enumeration and will appear as soon as you type
the equal sign.
Anchoring and Docking
A common issue in form design is the design of forms that are properly resized. For instance,
you might design a nice form for a given size, but when it’s resized at runtime, the controls are
all clustered in the top-left corner. Or a TextBox control that covers the entire width of the form
at design time suddenly ‘‘cringes’’ on the left when the user drags out the window. If the user
makes the form smaller than the default size, part of the TextBox could be invisible because it’s
outside the form. You can attach scroll bars to the form, but that doesn’t really help — who wantsPetroutsos c07.tex V3 - 01/28/2008 1:11pm Page 230
230 CHAPTER 7 WORKING WITH FORMS
to type text and have to scroll the form horizontally? It makes sense to scroll vertically because
you get to see many lines at once, but if the TextBox control is wider than the form, you can’t read
entire lines.
Anchoring Controls
The Anchor property lets you attach one or more edges of the control to corresponding edges of
the form. The anchored edges of the control maintain the same distance from the corresponding
edges of the form.
Place a TextBox control on a new form, set its MultiLine property to True, and then open
the control’s Anchor property in the Properties window. You will see a rectangle within a larger
rectangle and four pegs that connect the small control to the sides of the larger box (see Figure 7.6).
The large box is the form, and the small one is the control. The four pegs are the anchors, which
can be either white or gray. The gray anchors denote a fixed distance between the control and
the form. By default, the control is placed at a fixed distance from the top-left corner of the form.
When the form is resized, the control retains its size and its distance from the top-left corner of
the form.
Figure 7.6
The settings of the
Anchor property
We want our TextBox control to fill the width of the form, be aligned to the top of the form, and
leave some space for a few buttons at the bottom.We also want our form to maintain this arrange-
ment, regardless of its size. Make the TextBox control as wide as the form (allowing, perhaps, a
margin of a few pixels on either side). Then place a couple of buttons at the bottom of the form
and make the TextBox control tall enough that it stops above the buttons. This is the form of the
Anchor sample project.Petroutsos c07.tex V3 - 01/28/2008 1:11pm Page 231
THE APPEARANCE OF FORMS 231
Now open the TextBox control’s Anchor property and make all four anchors gray by clicking
them. This action tells the Form Designer to resize the control accordingly at runtime, so that the
distances between the sides of the control and the corresponding sides of the form are the same as
those you set at design time. Select each button on the form and set their Anchor properties in the
Properties window: Anchor the left button to the left and bottom of the form, and the right button
to the right and bottom of the form.
Resize the form at design time without running the project, and you’ll see that all the controls
are resized and rearranged on the form at all times. Figure 7.7 shows the Anchor project’s main
form in two different sizes.
Figure 7.7
Use the Anchor property
of the various controls
to design forms that can
be resized gracefully at
runtime.
Yet, there’s a small problem: If you make the form very narrow, there will be no room for both
buttons across the form’s width. The simplest way to fix this problem is to impose a minimum size
for the form. To do so, you must first decide the form’s minimum width and height and then set
the MinimumSize property to these values. You can also use the AutoScroll properties, but it’s
not recommended that you add scroll bars to a small form like ours.
Docking Controls
In addition to the Anchor property, most controls provide the Dock property, which determines
how a control will dock on the form. The default value of this property is None.
Create a new form, place a multiline TextBox control on it, and then open the control’s Dock
property. The various rectangular shapes are the settings of the property. If you click the middle
rectangle, the control will be docked over the entire form: It will expand and shrink both hori-
zontally and vertically to cover the entire form. This setting is appropriate for simple forms that
contain a single control, usually a TextBox, and sometimes a menu. Try it out.
Let’s create a more complicated form with two controls (see the Docking sample project). The
form shown in Figure 7.8 contains a TreeView control on the left and a ListView control on the
right. The two controls display folder and file data on an interface that’s very similar to that
of Windows Explorer. The TreeView control displays the directory structure, and the ListView
control displays the selected folder’s files.
Place a TreeView control on the left side of the form and a ListView control on the right side
of the form. Then dock the TreeView to the left and the ListView to the right. If you run thePetroutsos c07.tex V3 - 01/28/2008 1:11pm Page 232
232 CHAPTER 7 WORKING WITH FORMS
application now, as you resize the form, the two controls remain docked to the two sides of the
form — but their sizes don’t change. If you make the form wider, there will be a gap between the
two controls. If you make the form narrower, one of the controls will overlap the other.
Figure 7.8
Filling a form with two
controls
End the application, return to the Form Designer, select the ListView control, and set its Dock
property to Fill. This time, the ListView will change size to take up all the space to the right of
the TreeView. The ListView control will attempt to fill the form, but it won’t take up the space of
another control that has been docked already. The TreeView and ListView controls are discussed
in Chapter 9, ‘‘The TreeView and ListView Controls’’; that’s why I’ve populated them with some
fake data at design time. In Chapter 9, you’ll learn how to populate these two controls at runtime
with folder names and filenames, respectively, and build a custom Windows Explorer.
Splitting Forms into Multiple Panes
The form behaves better, but it’s not what you really expect from a Windows application. The
problem with the form in Figure 7.8 is that users can’t change the relative widths of the controls.
In other words, they can’t make one of the controls narrower to make room for the other, which is
a fairly common concept in theWindows interface.
The narrow bar that allows users to control the relative sizes of two controls is a splitter.When
the cursor hovers over a splitter, it changes to a double arrow to indicate that the bar can be
moved. By moving the splitter, you can enlarge one of the two controls while shrinking the other.
The Form Designer provides a special control for placing a splitter between two controls: the
SplitContainer control. We’ll design a new form with two TextBoxes and a splitter between them
so that users can change the relative size of the two controls.
First, place a SplitContainer control on the form. The SplitContainer consists of two Panels, the
Panel1 and Panel2 controls, and a vertical splitter between them. This is the default configuration;
you can change the orientation of the splitter by using the control’s Orientation property. Also
by default, the two panels of the Splitter control are resized proportionally as you resize the form.
If you want to keep one of the panels fixed and have the other take up the rest of the form, set the
control’s FixedPanel property to the name of the panel you want to retain its size.
Next, place a TextBox control in the left panel of the SplitControl and set its Multiline property
to True. You don’t need to do anything about its size because we’ll dock it in the panel to which itPetroutsos c07.tex V3 - 01/28/2008 1:11pm Page 233
THE APPEARANCE OF FORMS 233
belongs.With the TextBox control selected, locate its Dock property and set it to Fill.TheTextBox
control will fill the left panel of the SplitContainer control. Do the same with another TextBox
control, which will fill the right panel of the SplitContainer control. Set this control’s Multiline
property to True and its Dock property to Fill.
Now run the project and check out the functionality of the SplitContainer. Paste some text on
the two controls and then change their relative sizes by sliding the splitter between them, as shown
in Figure 7.9. You will find this project, called Splitter1, among the sample projects of this chapter.
Figure 7.9
The SplitContainer con-
trol lets you change the
relative size of the con-
trols on either side.
Let’s design a more elaborate form with two SplitContainer controls, such as the one shown in
Figure 7.10. (It’s the form in the Splitter2 sample project.) This form, which resembles the interface
of Microsoft Office Outlook, consists of a TreeView control on the left (where the folders are dis-
played), a ListView control (where the selected folder’s items are displayed), and a TextBox control
(where the selected item’s details are displayed). Because we haven’t discussed the ListView and
TreeView controls yet, I’m using three TextBox controls with different background colors; the
process of designing the form is identical, regardless of the controls you put on it.
Figure 7.10
An elaborate form with
two splitter controls
Start by placing a SplitContainer control on the form. Then place a multiline TextBox control on
the left panel of the SplitContainer control and set the TextBox control’s Dock property to Fill.The
TextBox control will fill the left panel of the SplitContainer control. Place another SplitContainerPetroutsos c07.tex V3 - 01/28/2008 1:11pm Page 234
234 CHAPTER 7 WORKING WITH FORMS
in the right panel of the first SplitContainer control. This control will be automatically docked in
its panel and will fill it. Its orientation, however, is vertical, and the splitter will separate the panel
into two smaller vertical panes. Select the second SplitContainer control, locate its Orientation
property in the Properties window, and set it to Horizontal.
Now you can fill each of the panels with a TextBox control. Set each TextBox control’s
BackgroundColor to a different color, its MultiLine property to True, and its Dock property
to Fill. The TextBox controls will fill their containers, which are the panels of the two SplitCon-
tainer controls, not the form. If you look up the properties of a SplitContainer control, you’ll see
that it’s made up of two Panel controls, which are exposed as properties of the SplitContainer
control, the Panel1 and Panel2 controls. You can set many of the properties of these two con-
stituent controls, such as their font and color, their minimum size, and so on. They even expose an
AutoScroll property, so that users can scroll the contents of each one independently of the other.
You can also set other properties of the SplitContainer control, such as the SplitterWidth prop-
erty, which is the width of the splitter bar between the two panels in pixels, and the Splitter-
Increment property, which is the smallest number of pixels that the splitter bar can be moved in
either direction.
So far, you’ve seen what the Form Designer and the Form object can do for your application.
Let’s switch our focus to programming forms and explore the events triggered by the Form object.
The Form’s Events
The Form object triggers several events. The most important are Activated, Deactivate, Form-
Closing, Resize,and Paint.
The Activated and Deactivate Events
When more than one form is displayed, the user can switch from one to the other by using the
mouse or by pressing Alt+Tab. Each time a form is activated, the Activated event takes place.
Likewise, when a form is activated, the previously active form receives the Deactivate event.
Insert in these two event handlers the code you want to execute when a form is activated (set
certain control properties, for example) and when a form loses the focus or is deactivated. These
two events are the form’s equivalents of the Enter and Leave events of the various controls.
Notice an inconsistency in the names of the two events: the Activated event takes place after
the form has been activated, whereas the Deactivate event takes place right before the form is
deactivated.
The FormClosing and FormClosed Events
The FormClosing event is fired when the user closes the form by clicking its Close button. If the
application must terminate because Windows is shutting down, the same event will be fired as
well. Users don’t always quit applications in an orderly manner, and a professional application
should behave gracefully under all circumstances. The same code you execute in the application’s
Exit command must also be executed from within the closing event. For example, you might
display a warning if the user has unsaved data, you might have to update a database, and so on.
Place the code that performs these tasks in a subroutine and call it from within your menu’s Exit
command, as well as from within the FormClosing event’s handler.
You can cancel the closing of a form by setting the e.Cancel property to True. The event han-
dler in Listing 7.2 displays a message box informing the user that the data hasn’t been saved and
gives him a chance to cancel the action and return to the application.Petroutsos c07.tex V3 - 01/28/2008 1:11pm Page 235
THE APPEARANCE OF FORMS 235
Listing 7.2: Cancelling the Closing of a Form
Public Sub Form1 FormClosing(...) Handles Me.FormClosing
Dim reply As MsgBoxResult
reply = MsgBox(”Document has been edited. ” &
”OK to terminate application, Cancel to ” &
”return to your document.”, MsgBoxStyle.OKCancel)
If reply = MsgBoxResult.Cancel Then
e.Cancel = True
End If
End Sub
The e argument of the FormClosing event provides the CloseReason property, which reports
how the form is closing. Its value is one of the following members of the CloseReason enumera-
tion: FormOwnerClosing, MdiFormClosing, None, TaskManagerClosing, WindowsShutDown.The
names of the members are self-descriptive, and you can query the CloseReason property to deter-
mine how the window is closing.
The FormClosed event fires after the form has been closed. You can find out the action that
caused the form to be closed through the e.CloseReason property, but it’s too late to cancel the
closing of the form.
The Resize, ResizeBegin,and ResizeEnd Events
The Resize event is fired every time the user resizes the form by using the mouse. With previous
versions of VB, programmers had to insert quite a bit of code in the Resize event’s handler to
resize the controls and possibly rearrange them on the form.With the Anchor and Dock properties,
much of this overhead can be passed to the form itself. If you want the two sides of the form
to maintain a fixed ratio, however, you have to resize one of the dimensions from within the
Resize event handler. Let’s say the form’s width-to-height ratio must be 3:4. Assuming that you’re
using the form’s height as a guide, insert the following statement in the Resize event handler to
make the width equal to three-fourths of the height:
Private Form1 Resize(...) Handles Me.Resize
Me.Width = (0.75 * Me.Height)
End Sub
The Resize event is fired continuously while the formis being resized. If youwant to keep track
of the initial form’s size and perform all the calculations after the user has finished resizing the
form, you can use the ResizeBegin and ResizeEnd events, which are fired at the beginning and
after the end of a resize operation, respectively. Store the form’s width and height to two global
variables in the ResizeBegin event and use these two variables in the ResizeEnd event handler.
The Scroll Event
The Scroll event is fired by forms that have their AutoScroll property set to True when the
user scrolls the form. The second argument of the Scroll event handler exposes the OldValue
and NewValue properties, which are the displacements of the form before and after the scroll
operation. This event can be used to keep a specific control in view when the form’s contents are
scrolled.Petroutsos c07.tex V3 - 01/28/2008 1:11pm Page 236
236 CHAPTER 7 WORKING WITH FORMS
The AutoScroll property is handy for large forms, but it has a serious drawback: It scrolls
the entire form. In most cases, we want to keep certain controls in view at all times. Instead of
a scrollable form, you can create forms with scrollable sections by exploiting the AutoScroll
properties of the Panel and/or the SplitContainer controls. You can also reposition certain controls
from within the form’s Scroll event handler. Let’s say you have placed a few controls on a Panel
container and you want to keep this Panel at the top of a scrolling form. The following statements
in the form’s Scroll event handler reposition the Panel at the top of the form every time the user
scrolls the form:
Private Sub Form1 Scroll(...) Handles Me.Scroll
Panel1.Top = Panel1.Top + (e.NewValue - e.OldValue)
End Sub
The Paint Event
This event takes place every time the form must be refreshed, and we use its handler to execute
code for any custom drawing on the form. When you switch to another form that partially or
totally overlaps the current one and then switch back to the first form, the Paint event will be
fired to notify your application that it must redraw the form. The form will refresh its controls
automatically, but any custom drawing on the form won’t be refreshed automatically. We’ll dis-
cuss this event in more detail in Chapter 18, ‘‘Drawing and Painting with Visual Basic 2008,’’ in
the presentation of the Framework’s drawing methods.
Loading and Showing Forms
Most practical applications are made up of multiple forms and dialog boxes, and one of the oper-
ations you’ll have to perform with multiform applications is to load and manipulate forms from
within other forms’ code. For example, you might want to display a second form to prompt the
user for data specific to an application. You must explicitly load the second form and read the
information entered by the user when the auxiliary form is closed. Or you might want to maintain
two forms open at once and let the user switch between them. A text editor and its Find & Replace
dialog box is a typical example.
You can access a form from within another form by its name. Let’s say that your application
has two forms, named Form1 and Form2, and that Form1 is the project’s startup form. To show
Form2 when an action takes place on Form1,callthe Show method of the auxiliary form:
Form2.Show
This statement brings up Form2 and usually appears in a button’s or menu item’s Click event
handler. To exchange information between two forms, use the techniques described in the ‘‘Con-
trolling One Form from within Another,’’ section later in this chapter.
The Show method opens a form in a modeless manner: The two forms are equal in stature on the
desktop, and the user can switch between them. You can also display the second form in a modal
manner, which means that users can’t return to the form from which they invoked it without
closing the second form. While a modal form is open, it remains on top of the desktop, and you
can’t move the focus to any other form of the same application (but you can switch to another
application). To open a modal form, use the ShowDialog method:
Form2.ShowDialog
The modal form is, in effect, a dialog box like the Open File dialog box. You must first select a
file on this form and click the Open button, or click the Cancel button to close the dialog box andPetroutsos c07.tex V3 - 01/28/2008 1:11pm Page 237
LOADING AND SHOWING FORMS 237
return to the form from which the dialog box was invoked. This brings up the topic of forms and
dialog boxes.
A dialog box is simply a modal form. When we display forms as dialog boxes, we change the
border of the forms to the setting FixedDialog and invoke them with the ShowDialog method.
Modeless forms are more difficult to program because the user may switch among them at any
time. Moreover, the two forms that are open at once must interact with one another. When the
user acts on one of the forms, it might necessitate some changes in the other, and you’ll see shortly
how this is done. If the two active forms don’t need to interact, display one of themas a dialog box.
When you’re finished with the second form, you can either close it by calling its Close method
or hide it by calling its Hide method. The Close method closes the form, and its resources are
returned to the system. The Hide method sets the form’s Visible property to False; you can still
access a hidden form’s controls from within your code, but the user can’t interact with it. Forms
that are displayed often, such as the Find & Replace dialog box of a text-processing application,
should be hidden — not closed. To the user, it makes no difference whether you hide or close a
form. If you hide a form, however, the next time you bring it up with the Show method, its controls
are in the state they were the last time.
The Startup Form
A typical application has more than a single form. When an application starts, the main form is
loaded. You can control which form is initially loaded by setting the startup object in the project
Properties window, shown in Figure 7.11. To open this dialog box, right-click the project’s name in
the Solution Explorer and select Properties. In the project’s Properties pages, select the Application
tab and select the appropriate item in the Startup Form combo box.
Figure 7.11
In the Properties win-
dow, you can specify the
form that’s displayed
when the application
starts.
By default, the IDE suggests the name of the first form it created, which is Form1.Ifyouchange
the name of the form, Visual Basic will continue using the same form as the startup form with its
new name.Petroutsos c07.tex V3 - 01/28/2008 1:11pm Page 238
238 CHAPTER 7 WORKING WITH FORMS
You can also start an application by using a subroutine, without loading a form. This sub-
routine is the MyApplication Startup event handler, which is fired automatically when the
application starts. To display the AuxiliaryForm object from within the Startup event handler,
use the following statement:
Private Sub MyApplication Startup(...) Handles Me.Startup
System.Windows.Forms.Application.Run(New AuxiliaryForm())
End Sub
To view the MyApplication Startup event handler, click the View Application Events button
at the bottom of the Application pane, as shown in Figure 7.11. This action will take you to the
MyApplication code window, where you can select the MyApplication Events item in the object
list and the Startup item in the events list.
Controlling One Form from within Another
Loading and displaying a formfromwithin another form’s code is fairly trivial. In some situations,
this is all the interaction you need between forms. Each form is designed to operate independently
of the others, but they can communicate via public variables (see the following section). In most
situations, however, you need to control one form from within another’s code. Controlling the
form means accessing its controls, and setting or reading values from within another form’s code.
In Chapter 6, you developed the TextPad application, which is a basic text editor and consists
of the main form and an auxiliary form for the Find & Replace operations. All other operations on
the text are performed with menu commands on the main form.When the user wants to search for
and/or replace a string, the program displays another form on which the user specifies the text to
find, the type of search, and so on. When the user clicks one of the Find & Replace form’s buttons,
the corresponding code must access the text on the main form of the application and search for
a word or replace a string with another. The Find & Replace dialog box not only interacts with
the TextBox control on the main form, it also remains visible at all times while it’s open, even if it
doesn’t have the focus, because its TopMost property was set to True.
Sharing Variables between Forms
The preferred method for two forms to communicate with each other is via public variables.These
variables are declared in the form’s declarations section, outside any procedure, with the key-
word Public. If the following declarations appear in Form1,thevariable NumPoints and the array
DataValues can be accessed by any procedure in Form1,aswellasfromwithinthecodeofany
form belonging to the same project:
Public NumPoints As Integer
Public DataValues(100) As Double
To access a public variable declared in Form1 from within another form’s code, you must prefix
the variable’s name by the name of the form, as in the following:
Form1.NumPoints = 99
Form1.DataValues(0) = 0.3395022Petroutsos c07.tex V3 - 01/28/2008 1:11pm Page 239
LOADING AND SHOWING FORMS 239
You can use the same notation to access the controls on another form. If Form1 contains the
TextBox1 control, you can use the following statement to read its text:
Form1.TextBox1.Text
If a button on Form1 opens the auxiliary form Form2, you can set selected controls to specific
values before showing the auxiliary form. The following statements should appear in a button’s
or menu item’s Click event handler:
Form2.TextBox1.Text = ”some text”
Form2.DateTimePicker1.Value = Today
Form2.Show()
You can also create a variable to represent another form and access the auxiliary form through
this variable. Let’s say you want to access the resources of Form2 from within the code of Form1.
Declare the variable auxForm to represent Form2 and then access the resources of Form2 with the
following statements:
Dim auxForm As Form2
auxForm.TextBox1.Text = ”some text”
auxForm.DateTimePicker1.Value = Today
auxForm.Show
Multiple Instances of a Single Form
Note that the variable that represents an auxiliary form is declared without the New keyword. The
auxForm variable represents an existing form. If we used the New keyword, we’d create a new
instance of the corresponding form. This technique is used when we want to display multiple
instances of the same form, as in an application that allows users to open multiple documents of the
same type.
Let’s say you’re designing an image-processing application, or a simple text editor. Each new docu-
ment should be opened in a separate window. Obviously, we can’t design many identical forms and
use them as needed. The solution is to design a single form and create new instances of it every time
the user opens an existing document or creates a new one. These instances are independent of one
another and they may interact with the main form. Usually they don’t, because they aren’t auxiliary
forms; they contain the necessary interface elements, such as menus, for processing the specific doc-
ument type, and users can arrange them any way they like on the desktop.
The approach described here is reminiscent of Multiple Document Interface (MDI) applications. The
MDI interface requires that all windows be contained within a parent window and, although once
very popular, it’s going slowly out of style. The new interfaces open multiple independent windows
on the desktop. Each window is an instance of a single form and it’s declared with the New keyword.
I’ve used this style of interface to redesign the TextPad application of Chapter 6, and I’ve included the
revised application in this chapter’s projects for your reference. Open the project in Visual Studio and
examine its code, which contains a lot of comments.Petroutsos c07.tex V3 - 01/28/2008 1:11pm Page 240
240 CHAPTER 7 WORKING WITH FORMS
Forms versus Dialog Boxes
Dialog boxes are special types of forms with very specific functionality, which we use to prompt
the user for data. The Open and Save dialog boxes are two of the most familiar dialog boxes in
Windows. They’re so common that they’re actually known as common dialog boxes. Technically, a
dialog box is a good old form with its FormBorderStyle property set to FixedDialog. Like forms,
dialog boxes might contain a few simple controls, such as Labels, TextBoxes, and Buttons. You
can’t overload a dialog box with controls and functionality, because you’ll end up with a regular
form.
Figure 7.12 shows a few dialog boxes you have certainly seen while working with Windows
applications. The Insert Caption dialog box ofWord is amodal dialog box: Youmust close it before
switching to your document. The Find & Replace dialog box is modeless: It allows you to switch
to your document, yet it remains visible while open even if it doesn’t have the focus.
Figure 7.12
Typical dialog boxes
used by Word
Notice that some dialog boxes, such as Open, Color, and even the humble MessageBox, come
with the Framework, and you can incorporate them in your applications without having to design
them.
A characteristic of dialog boxes is that they provide an OK and a Cancel button. The OK button
tells the application that you’re finished using the dialog box, and the application can process the
information in it. The Cancel button tells the application that it should ignore the information in
the dialog box and cancel the current operation. As you will see, dialog boxes allow you to quickly
find out which buttons were clicked to close them, so that your application can take a different
action in each case.
In short, the difference between forms and dialog boxes is artificial. If it were really important
to distinguish between the two, they’d be implemented as two different objects— but they’re the
same object. So, without any further introduction, let’s look at how to create and use dialog boxes.
To create a dialog box, start with a Windows form, set its FormBorderStyle property to Fixed-
Dialog, and set the ControlBox, MinimizeBox,and MaximizeBox properties to False. Then add the
necessary controls on the form and code the appropriate events, as you would do with a regular
Windows form.
Figure 7.13 shows a simple dialog box that prompts the user for an ID and a password (see the
Password sample project). The dialog box contains two TextBox controls, next to the appropriate
labels, and the usual OK and Cancel buttons. The Cancel button signifies that the user wants to
cancel the operation, which was initiated in the form that displayed the dialog box.
Start a new project, rename the form to MainForm, and place a button on the form. This is the
application’s main form, and we’ll invoke the dialog box from within the button’s Click eventPetroutsos c07.tex V3 - 01/28/2008 1:11pm Page 241
LOADING AND SHOWING FORMS 241
handler. Then add a new form to the project, name it PasswordForm, and place on it the controls
shown in Figure 7.13.
Figure 7.13
A simple dialog box
that prompts users for a
username and password
To display a modal form, you call the ShowDialog method, instead of the Show method. You
already know how to read the values entered on the controls of the dialog box. You also need
to know which button was clicked to close the dialog box. To convey this information from the
dialog box back to the calling application, the Form object provides the DialogResult property.
This property can be set to one of the values shown in Table 7.3 to indicate which button was
clicked. The DialogResult.OK value indicates that the user has clicked the OK button on the form.
There’s no need to place an OK button on the form; just set the form’s DialogResult property to
DialogResult.OK.
Table 7.3: The DialogResult Enumeration
Value Description
Abort The dialog box was closed with the Abort button.
Cancel The dialog box was closed with the Cancel button.
Ignore The dialog box was closed with the Ignore button.
No The dialog box was closed with the No button.
None The dialog box hasn’t been closed yet. Use this option to find out whether a modeless dialog
box is still open.
OK The dialog box was closed with the OK button.
Retry The dialog box was closed with the Retry button.
Yes The dialog box was closed with the Yes button.Petroutsos c07.tex V3 - 01/28/2008 1:11pm Page 242
242 CHAPTER 7 WORKING WITH FORMS
The dialog box need not contain any of the buttons mentioned here. It’s your responsibility to
set the value of the DialogResult property from within your code to one of the settings shown in
the table. This value can be retrieved by the calling application. The code behind the two buttons
in the dialog box is quite short:
Private Sub bttnOK Click(...) Handles bttnOK.Click
Me.DialogResult = DialogResult.OK
Me.Close
End Sub
Private Sub bttnCancel Click(...) Handles bttnCancel.Click
Me.DialogResult = DialogResult.Cancel
Me.Close
End Sub
The event handler of the button that displays this dialog box should contain an If statement
that examines the value returned by the ShowDialog method:
If PasswordForm.ShowDialog = DialogResult.OK Then
{ process the user selection }
End If
Depending on your application, you might allow the user to close the dialog box by clicking
more than two buttons. Some of them must set the DialogResult property to DialogResult.OK,
others to DialogResult.Cancel.
If the form contains an Accept and a Cancel button, you don’t have to enter a single line of code
in the modal form. The user can enter values on the various controls and then close the dialog
box by pressing the Enter or Cancel key. The dialog box will close and will return the Dialo-
gResult.OK or DialogResult.Cancel value. The Accept button sets the form’s DialogResult
property to DialogResult.OK automatically, and the Cancel button sets the same property to
DialogResult.Cancel. Any other button must set the DialogResult property explicitly. Listing
7.3 shows the code behind the Log In button on the sample project’s main form.
Listing 7.3: Prompting the User for an ID and a Password
Private Sub Button1 Click(...) Handles Button1.Click
If PasswordForm.ShowDialog() = DialogResult.OK Then
If PasswordForm.txtUserID.Text = ”” Or
PasswordForm.txtPassword.Text = ”” Then
MsgBox(”Please specify a user ID and a password to connect”)
Exit Sub
End If
MsgBox(”You were connected as ” &
passwordForm.txtUserID.Text)
ElsePetroutsos c07.tex V3 - 01/28/2008 1:11pm Page 243
LOADING AND SHOWING FORMS 243
MsgBox(”Connection failed for user ” &
password.txtPassword.Text)
End If
End Sub
VB 2008 at Work: The MultipleForms Project
It’s time to write an application that puts together the most important topics discussed in this
section. The MultipleForms project consists of a main form, an auxiliary form, and a dialog box.
All three components of the application’s interface are shown in Figure 7.14. The buttons on the
main form display both the auxiliary form and the dialog box.
Figure 7.14
The MultipleForms
project’s interface
Let’s review the various operations we want to perform — they’re typical for many situations,
not for only this application. At first, we must be able to invoke both the auxiliary form and the
dialog box from within the main form; the Show Auxiliary Form and Show Dialog Box buttons
do this. The main form contains a variable declaration: strProperty. This variable is, in effect, a
property of the main form and is declared as public with the following statement:
Public strProperty As String = ”Mastering VB 2008”
The main form calls the auxiliary form’s Show method to display it in a modeless manner. The
auxiliary form button named Read Shared Variable In Main Form reads the strProperty variable
of the main form with the following statement:
Private Sub bttnReadShared Click(...) Handles bttnReadShared.Click
MsgBox(MainForm.strProperty, MsgBoxStyle.OKOnly,
”Public Variable Value”)
End SubPetroutsos c07.tex V3 - 01/28/2008 1:11pm Page 244
244 CHAPTER 7 WORKING WITH FORMS
Using the same notation, you can set this variable from within the auxiliary form. The follow-
ing event handler prompts the user for a new value and assigns it to the shared variable of the
main form:
Private Sub bttnSetShared Click(...) Handles bttnSetShared.Click
Dim str As String
str = InputBox(”Enter a new value for strProperty”)
MainForm.strProperty = str
End Sub
The two forms communicate with each other through public variables. Let’s make this commu-
nication a little more elaborate by adding an event. Every time the auxiliary form sets the value of
the strProperty variable, it will raise an event to notify the main form. The main form, in turn,
will use this event to display the new value of the string on the TextBox control as soon as the code
in the auxiliary form changes the value of the variable and before it’s closed.
To raise an event, you must declare the event’s name in the form’s declaration section. Insert
the following statement in the auxiliary form’s declarations section:
Event strPropertyChanged()
Now add a statement that fires the event. To raise an event, we call the RaiseEvent statement,
passing the name of the event as an argument. This statement must appear in the Click event
handler of the Set Shared Variable In Main Form button, right after setting the value of the shared
variable. Listing 7.4 shows the revised event handler.
Listing 7.4: Raising an Event
Private Sub bttnSetShared Click(...) Handles bttnSetShared.Click
Dim str As String
str = InputBox(”Enter a new value for strProperty”)
MainForm.strProperty = str
RaiseEvent strPropertyChanged
End Sub
The event will be raised, but it will go unnoticed if we don’t handle it from within the main
form’s code. To handle the event, you must create a variable that represents the auxiliary form
with the WithEvents keyword:
Dim WithEvents FRM As New AuxiliaryForm()
The WithEvents keyword tells VB that the variable is capable of raising events. If you
expand the drop-down list with the objects in the code editor, you will see the name of the FRM
variable, along with the other controls you can program. Select FRM in the list and then expand
the list of events for the selected item. In this list, you will see the strPropertyChanged event.Petroutsos c07.tex V3 - 01/28/2008 1:11pm Page 245
LOADING AND SHOWING FORMS 245
Select it, and the definition of an event handler will appear. Enter these statements in this event’s
handler:
Private Sub FRM strPropertyChanged() Handles FRM.strPropertyChanged
TextBox1.Text = strProperty
Beep()
End Sub
It’s a simple handler, but it’s adequate for demonstrating how to raise and handle custom
events on the form level. If you want, you can pass arguments to the event handler by includ-
ing them in the declaration of the event. To pass the original and the new value through the
strPropertyChanged event, use the following declaration:
Event strPropertyChanged(ByVal oldValue As String,
ByVal newValue As String)
If you run the application now, you’ll see that the value of the TextBox control in the main form
changes as soon as you change the property’s value in the auxiliary form.
Of course, you can update the TextBox control on the main form directly from within the
auxiliary form’s code. Use the expression MainForm.TextBox1 to access the control and then
manipulate it as usual. Events are used to perform some actions on a form when an action takes
place in one of the other forms of the application. The benefit of using events, as opposed to
accessing members of another form from within our code, is that the auxiliary form need not
know anything about the form that called it. The auxiliary form raises the event, and it’s the other
form’s responsibility to handle it.
Let’s see now how the main form interacts with the dialog box. What goes on between a form
and a dialog box is not exactly interaction; it’s a more timid type of behavior. The form displays
the dialog box and waits until the user closes the dialog box. Then it looks at the value of the
DialogResult property to find out whether it should even examine the values passed back by
the dialog box. If the user has closed the dialog box with the Cancel (or an equivalent) button, the
application ignores the dialog box settings. If the user closed the dialog box with the OK button,
the application reads the values and proceeds accordingly.
Before showing the dialog box, the code of the Show Dialog Box button sets the values of certain
controls in it. In the course of the application, it usually makes sense to suggest a few values in the
dialog box, so that the user can accept the default values by pressing the Enter key. The main form
selects a date on the dialog box’s controls and then displays the dialog box with the statements
given in Listing 7.5.
Listing 7.5: Displaying a Dialog Box and Reading Its Values
Protected Sub Button3 Click(...) Handles Button3.Click
’ Preselects the date 4/11/1980
AgeDialog.cmbMonth.Text = ”4”
AgeDialog.cmbDay.Text = ”11”
AgeDialog.CmbYear.Text = ”1980”Petroutsos c07.tex V3 - 01/28/2008 1:11pm Page 246
246 CHAPTER 7 WORKING WITH FORMS
AgeDialog.ShowDialog()
If AgeDialog.DialogResult = DialogResult.OK Then
MsgBox(AgeDialog.cmbMonth.Text & ” ” &
AgeDialog.cmbDay.Text & ”, ” &
AgeDialog.cmbYear.Text)
Else
MsgBox(”OK, we’ll protect your vital personal data”)
End If
End Sub
To close the dialog box, you can click the OK or Cancel button. Each button sets the DialogRe-
sult property to indicate the action that closed the dialog box. The code behind the two buttons
is shown in Listing 7.6.
Listing 7.6: Setting the Dialog Box’s DialogResult Property
Protected Sub bttnOK Click(...) Handles bttnOK.Click
Me.DialogResult = DialogResult.OK
End Sub
Protected Sub bttnCancel Click(...) Handles bttnCancel.Click
Me.DialogResult = DialogResult.Cancel
End Sub
Because the dialog box is modal, the code in the Show Dialog Box button is suspended at the
line that shows the dialog box. As soon as the dialog box is closed, the code in the main form
resumes with the statement following the one that called the ShowDialog method of the dialog
box. This is the If statement in Listing 7.5 that examines the value of the DialogResult property
and acts accordingly.
BuildingDynamic Forms at Runtime
Sometimes you won’t know in advance how many instances of a given control might be required
on a form. Let’s say you’re designing a form for displaying the names of all tables in a database.
It’s practically impossible to design a form that will accommodate every database users might
throw at your application. Another typical example is a form for entering family related data,
which includes the number of children in the family and their ages. As soon as the user enters
(or changes) the number of children, you should display as many TextBox controls as there are
children to collect their ages.
For these situations, it is possible to design dynamic forms, which are populated at runtime.
The simplest approach is to create more controls than you’ll ever need and set their Visible
properties to False at design time. At runtime, you can display the controls by switching their
Visible properties to True. As you know already, quick-and-dirty methods are not the most
efficient ones. You must still rearrange the controls on the form to make it look nice at all times.
The proper method to create dynamic forms at runtime is to add controls to and remove them
from your form as needed, using the techniques discussed in this section.Petroutsos c07.tex V3 - 01/28/2008 1:11pm Page 247
BUILDING DYNAMIC FORMS AT RUNTIME 247
Just as you can create new instances of forms, you can also create new instances of any control
and place them on a form. The Form object exposes the Controls collection, which contains all
the controls on the form. This collection is created automatically as you place controls on the form
at design time, and you can access the members of this collection from within your code. It is also
possible to add new members to the collection, or remove existing members, with the Add and
Remove statements accordingly.
The Form’s Controls Collection
All the controls on a form are stored in the Controls collection, which is a property of the Form
object. The Controls collection exposes members for accessing and manipulating the controls at
runtime, and they’re the usual members of a collection:
Add method The Add method adds a new element to the Controls collection. In effect, it
adds a new control on the current form. The Add method accepts a reference to a control as
an argument and adds it to the collection. Its syntax is the following, where controlObj is an
instance of a control:
Controls.Add(controlObj)
To place a new Button control on the form, declare a variable of the Button type, set its proper-
ties, and then add it to the Controls collection:
Dim bttn As New System.WinForms.Button
bttn.Text = ”New Button”
bttn.Left = 100
bttn.Top = 60
bttn.Width = 80
Me.Controls.Add(bttn)
Remove method The Remove method removes an element from the Controls collection. It
accepts as an argument either the index of the control to be removed or a reference to the con-
trol to be removed (a variable of the Control type that represents one of the controls on the
form). The syntax of these two forms of the Remove method is the following:
Me.Controls.Remove(index)
Me.Controls.Remove(controlObj)
Count property This property returns the number of elements in the Controls collection.
Notice that if there are container controls, the controls in the containers are not included in the
count. For example, if your form contains a Panel control, the controls on the panel won’t be
included in the value returned by the Count property. The Panel control, however, has its own
Controls collection.
All method This method returns all the controls on a form (or on a container control) as
an array of the System.WinForms.Control type. You can iterate through the elements of this
array with the usual methods exposed by the Array class.Petroutsos c07.tex V3 - 01/28/2008 1:11pm Page 248
248 CHAPTER 7 WORKING WITH FORMS
Clear method The Clear method removes all the elements of the Controls array and
effectively clears the form.
The Controls collection is also a property of any control that can host other controls. Many
of the controls that come with VB 2008, such as the Panel control, can host other controls. As you
recall from our discussion of the Anchor and Dock properties, it’s customary to place controls
on a panel and handle them collectively as a section of the form. They are moved along with
the panel at design time, and they’re rearranged as a group at runtime. The panel belongs to the
form’s Controls collection, and it provides its own Controls collection, which lets you access the
controls on the panel.
VB 2008 at Work: The ShowControls Project
The ShowControls project (Figure 7.15) demonstrates the basic methods of the Controls array.
Open the project and add any number of controls on its main form. You can place a panel to act as
a container for other controls as well. Just don’t remove the button at the top of the form (the Scan
Controls On This Form button), which contains the code to list all the controls.
Figure 7.15
Accessing the controls
on a form at runtime
The code behind the Scan Controls On This Form button enumerates the elements of the form’s
Controls collection. The code doesn’t take into consideration containers within containers. This
would require a recursive routine, which would scan for controls at any depth. The code that
iterates through the form’s Controls collection and prints the names of the controls in the Output
window is shown in Listing 7.7.
Listing 7.7: Iterating the Controls Collection
Private Sub Button1 Click(...) Handles Button1.Click
Dim Control As Windows.Forms.Control
For Each Control In Me.Controls
Debug.WriteLine(Control.ToString)
If Control.GetType Is GetType(System.Windows.Forms.Panel) ThenPetroutsos c07.tex V3 - 01/28/2008 1:11pm Page 249
BUILDING DYNAMIC FORMS AT RUNTIME 249
Dim nestedControl As Windows.Forms.Control
For Each nestedControl In Control.Controls
Debug.WriteLine(” ” & nestedControl.ToString)
Next
End If
Next
End Sub
The form shown in Figure 7.15 produced the following (partial) output (the controls on the
Panel are indented to stand out in the listing):
Panel1: System.Windows.Forms.Panel,
BorderStyle:
System.Windows.Forms.BorderStyle.FixedSingle
CheckBox4: System.Windows.Forms.CheckBox, CheckState: 0
CheckBox3: System.Windows.Forms.CheckBox, CheckState: 0
HScrollBar1: System.Windows.Forms.HScrollBar,
Minimum: 0, Maximum: 100, Value: 0
CheckedListBox1: System.Windows.Forms.CheckedListBox,
Items.Count: 3, Items[0]: Item 1
TextBox2: System.Windows.Forms.TextBox,
Text: TextBox2
TextBox1: System.Windows.Forms.TextBox,
Text: TextBox1
Button4: System.Windows.Forms.Button,
Text: Button4
To find out the type of individual controls, call the GetType method. The following statement
examines whether the control in the first element of the Controls collection is a TextBox:
If Me.Controls(0).GetType Is GetType(system.WinForms.TextBox) Then
MsgBox(”It’s a TextBox control”)
End If
Notice the use of the Is operator in the preceding statement. The equals operator will cause
an exception because objects can be compared only with the Is operator. (You’re comparing
instances, not values.)
If you know the type’s exact name, you can use a statement like the following:
If Me.Controls(i).GetType.Name = ”TextBox” Then ...
To access other properties of the control represented by an element of the Controls collection,
you must first cast it to the appropriate type. If the first control of the collection is a TextBox con-
trol, use the CType() function to cast it to a TextBox variable and then request its Text property:
If Me.Controls(0).GetType Is GetType(system.WinForms.TextBox) Then
Debug.WriteLine(CType(Me.Controls(0), TextBox).Text)
End IfPetroutsos c07.tex V3 - 01/28/2008 1:11pm Page 250
250 CHAPTER 7 WORKING WITH FORMS
The If statement is necessary, unless you can be sure that the first control is a TextBox control.
If you omit the If statement and attempt to convert the control to a TextBox, a runtime exception
will be thrown if the object Me.Controls(0) isn’t a TextBox control.
VB 2008 at Work: The DynamicForm Project
To demonstrate how to handle controls at runtime from within your code, I included the
DynamicFormproject (Figure 7.16), a simple data-entrywindow for a small number of data points.
The user can specify at runtime the number of data points she wants to enter, and the number of
TextBoxes on the form is adjusted automatically.
Figure 7.16
The DynamicForm
project
The control you see at the top of the form is the NumericUpDown control. All you really need
to know about this control is that it displays an integer in the range specified by its Minimum
and Maximum properties and allows users to select a value. It also fires the ValueChanged event
every time the user clicks one of the two arrows or types another value in its edit area. This event
handler’s code adds or removes controls on the form, so that the number of text boxes (as well
as the number of corresponding labels) matches the value on the control. Listing 7.8 shows the
handler for the ValueChanged event of the NumericUpDown1 control. The ValueChanged event is
fired when the user clicks one of the two arrows on the control or types a new value in the control’s
edit area.
Listing 7.8: Adding and Removing Controls at Runtime
Private Sub NumericUpDown1 ValueChanged(...) Handles NumericUpDown1.ValueChanged
Dim TB As New TextBox()
Dim LBL As New Label()
Dim i, TBoxes As Integer
’ Count all TextBox controls on the Form
For i = 0 To Me.Controls.Count - 1
If Me.Controls(i).GetType Is
GetType(System.Windows.Forms.TextBox) Then
TBoxes = TBoxes + 1
End IfPetroutsos c07.tex V3 - 01/28/2008 1:11pm Page 251
BUILDING DYNAMIC FORMS AT RUNTIME 251
Next
’ Add new controls if number of controls on the Form is less
’ than the number specified with the NumericUpDown control
If TBoxes < NumericUpDown1.Value Then
TB.Left = 100
TB.Width = 120
TB.Text = ””
For i = TBoxes To CInt(NumericUpDown1.Value) - 1
TB = New TextBox()
LBL = New Label()
If NumericUpDown1.Value = 1 Then
TB.Top = 20
TB.TabIndex = 0
Else
TB.Top = Me.Controls(Me.Controls.Count - 2).Top + 25
End If
’ Set the trivial properties of the new controls
LBL.Left = 20
LBL.Width = 80
LBL.Text = ”Data Point ” & i
LBL.Top = TB.Top + 3
TB.Left = 100
TB.Width = 120
TB.Text = ””
’ add controls to the form
Me.Controls.Add(TB)
Me.Controls.Add(LBL)
TB.TabIndex = Convert.ToInt32(NumericUpDown1.Value)
’ and finally connect their GotFocus/LostFocus events
’ to the appropriate handler
AddHandler TB.Enter,
New System.EventHandler(AddressOf TBox Enter)
AddHandler TB.Leave,
New System.EventHandler(AddressOf TBox Leave)
Next
Else
For i = Me.Controls.Count - 1 To Me.Controls.Count -
2 * (TBoxes - CInt(NumericUpDown1.Value)) Step -2
Me.Controls.Remove(Controls(i))
Me.Controls.Remove(Controls(i - 1))
Next
End If
End Sub
Ignore the AddHandler statements for now; they’re discussed in the following section. First,
the code counts the number of TextBoxes on the form; then it figures out whether it should add
or remove elements from the Controls collection. To remove controls, the code iterates through
the last n controls on the form and removes them. The number of controls to be removed is thePetroutsos c07.tex V3 - 01/28/2008 1:11pm Page 252
252 CHAPTER 7 WORKING WITH FORMS
following, where TBoxes is the total number of controls on the form minus the value specified in
the NumericUpDown control:
2 * (TBoxes - NumericUpDown1.Value)
If the value entered in the NumericUpDown control is less than the number of TextBox controls
on the form, the code removes the excess controls from within a loop. At each step, it removes two
controls, one of them a TextBox and the other a Label control with the matching caption. (That’s
why the loop variable is decreased by two.) The code also assumes that the first two controls on the
form are the Button and the NumericUpDown controls. If the value entered by the user exceeds
the number of TextBox controls on the form, the code adds the necessary pairs of TextBox and
Label controls to the form.
To add controls, the code initializes a TextBox (TB)andaLabel(LBL) variable. Then, it sets
their locations and the label’s caption. The left coordinate of all labels is 20, their width is 80, and
their Text property (the label’s caption) is the order of the data item. The vertical coordinate is
20 pixels for the first control, and all other controls are 3 pixels below the control on the previous
row. After a new control is set up, it’s added to the Controls collection with one of the following
statements:
Me.Controls.Add(TB) ’ adds a TextBox control
Me.Controls.Add(LBL) ’ adds a Label control
The code contains a few long lines, but it isn’t really complicated. It’s based on the assump-
tion that except for the first few controls on the form, all others are pairs of Label and TextBox
controls used for data entry. You can simplify the code a little by placing the Label and Text-
Box controls on a Panel and manipulate the Panel’s Controls collection. This collection contains
only the data-entry controls, and the form may contain any number of additional controls.
To use the values entered by the user on the dynamic form, we must iterate the Controls col-
lection, extract the values in the TextBox controls, and use them. Listing 7.9 shows how the top
Process Values button scans the TextBox controls on the form and performs some basic calcula-
tions with them (counting the number of data points and summing their values).
Listing 7.9: Reading the Controls on the Form
Private Sub Button1 Click(...) Handles Button1.Click
Dim TBox As TextBox
Dim Sum As Double = 0, points As Integer = 0
Dim iCtrl As Integer
For iCtrl = 0 To Me.Controls.Count - 1
If Me.Controls(iCtrl).GetType Is
GetType(System.Windows.Forms.TextBox) Then
TBox = CType(Me.Controls(iCtrl), TextBox)
If IsNumeric(TBox.Text) Then
Sum = Sum + Val(TBox.Text)
points = points + 1
End If
End IfPetroutsos c07.tex V3 - 01/28/2008 1:11pm Page 253
BUILDING DYNAMIC FORMS AT RUNTIME 253
Next
MsgBox(”The sum of the ” & points.ToString &
” data points is ” & Sum.ToString)
End Sub
You can add more statements to calculate the mean and other vital statistics, or you can process
the values in any other way. You can even dump all the values into an array and then use the array
notation to manipulate them.
The project’s form has its AutoScroll property set to True, so that users can scroll it up and
down if they specify a number of data points that exceeds the vertical dimension of the form. The
two controls on the top-right side of the form, however, must remain at their location at all times.
I placed them on a Panel control and added some code to the form’s Scroll event handler, so that
every time the user scrolls the form, the Panel control maintains its distance from the top and right
edges of the form (otherwise, the two controls would scroll out of view). A single statement is all
it takes to keep the Panel control in view at all times:
Private Sub Form1 Scroll(ByVal sender As Object,
ByVal e As System.Windows.Forms.ScrollEventArgs)
Handles Me.Scroll
Panel1.Top = Panel1.Top + (e.NewValue - e.OldValue)
End Sub
You should try to redesign this application and place the data-entry controls on a Panel with
its AutoSize and AutoScroll properties set to True.
The second button on the form does the exact same thing as the top one, only this one uses a
For Each ... Next loop structure to iterate through the form’s controls.
Creating Event Handlers at Runtime
You saw how to add controls on your forms at runtime and how to access the properties of these
controls from within your code. In many situations, this is all you need: a way to access the prop-
erties of the controls (the text on a TextBox control or the status of a CheckBox or RadioButton
control). What good is a Button control, however, if it can’t react to the Click event? The only
problem with the controls you add to the Controls collection at runtime is that they don’t react
to events. It’s possible, though, to create event handlers at runtime, and this is what you’ll learn in
this section.
To create an event handler at runtime, create a subroutine that accepts two arguments — the
usual sender and e arguments — and enter the code you want to execute when a specific control
receives a specific event. The type of the e argument must match the definition of the second
argument of the event for which you want to create a handler. Let’s say that you want to add one
ormore buttons at runtime on your form, and these buttons should react to the Click event. Create
the ButtonClick() subroutine and enter the appropriate code in it. The name of the subroutine
can be anything; you don’t have to make up a name that includes the control’s or the event’s name.
After the subroutine is in place, you must connect it to an event of a specific control. The But-
tonClick() subroutine, for example, must be connected to the Click event of a Button control.
The statement that connects a control’s event to a specific event handler is the AddHandler state-
ment, whose syntax is as follows:
AddHandler control.event, New System.EventHandler(AddressOf ButtonClick)Petroutsos c07.tex V3 - 01/28/2008 1:11pm Page 254
254 CHAPTER 7 WORKING WITH FORMS
For example, to connect the ProcessNow() subroutine to the Click event of the Calculate
button, use the following statement:
AddHandler Calculate.Click,
New System.EventHandler(AddressOf ProcessNow)
Let’s add a little more complexity to the DynamicForm application. We’ll program the Enter
and Leave events of the TextBox controls added at runtime through the Me.Controls.Add method.
When a TextBox control receives the focus, we’ll change its background color to a light yellow, and
when it loses the focus, we’ll restore the background to white, so the user knows which box has
the focus at any time. We’ll use the same handlers for all TextBox controls. (The code of the two
handlers is shown in Listing 7.10.)
Listing 7.10: Event Handlers Added at Runtime
Private Sub TBox Enter(ByVal sender As Object,
ByVal e As System.EventArgs)
CType(sender, TextBox).BackColor = color.LightCoral
End Sub
Private Sub TBox Leave(ByVal sender As Object,
ByVal e As System.EventArgs)
CType(sender, TextBox).BackColor = color.White
End Sub
The two subroutines use the sender argument to find out which TextBox control received or
lost the focus, and they set the appropriate control’s background color. (These subroutines are not
event handlers yet, because they’re not followed by the Handles keyword— at least, not before
we associate them with an actual control and a specific event.) This process is done in the same
segment of code that sets the properties of the controls we create dynamically at runtime. After
adding the control to the Me.Controls collection, call the following statements to connect the new
control’s Enter and Leave events to the appropriate handlers:
AddHandler TB.Enter, New System.EventHandler(AddressOf TBox Enter)
AddHandler TB.Leave, New System.EventHandler(AddressOf TBox Leave)
Run the DynamicForm application and see how the TextBox controls handle the focus-related
events. With a few statements and a couple of subroutines, we were able to create event handlers
at runtime from within our code.
Designing an Application Generator
In the preceding sections of this chapter, you learned how to create new forms from within your code
and how to instantiate them. In effect, you have the basic ingredients for designing applications from
within your code. Designing an application programmatically is not a trivial task, but now you have a
good understanding of how an application generator works. You can even design a wizard that
prompts the user for information about the appearance of the form and then design the form from
within your code.Petroutsos c07.tex V3 - 01/28/2008 1:11pm Page 255
DESIGNING MENUS 255
DesigningMenus
Menus are among the most common and most characteristic elements of the Windows user inter-
face. Even in the old days of character-based displays, menus were used to display methodically
organized choices and guide the user through an application. Despite the visually rich interfaces
of Windows applications and the many alternatives, menus are still the most popular means of
organizing a large number of options. Many applications duplicate some or all of their menus in
the form of toolbar icons, but the menu is a standard fixture of a form. You can turn the toolbars
on and off, but not the menus.
The Menu Editor
Menus can be attached only to forms, and they’re implemented through the MenuStrip control.
The items that make up the menu are ToolStripMenuItem objects. As you will see, the MenuStrip
control and ToolStripMenuItem objects give you absolute control over the structure and appear-
ance of the menus of your application. The MenuStrip control is a variation of the Strip control,
which is the base of menus, toolbars, and status bars.
You can design menus visually and then program their Click event handlers. In principle,
that’s all there is to a menu: You specify its items (the menu’s commands) and then you program
each command’s actions. Depending on the needs of your application, you might want to enable
and disable certain commands, add context menus to some of the controls on your form, and so
on. Because each item in a menu is represented by a ToolStripMenuItem object, you can control
the application’s menus from within your code by manipulating the properties of the ToolStrip-
MenuItem objects. Let’s start by designing a simple menu, and I’ll show you how to manipulate
the menu objects from within your code as we go along.
Double-click the MenuStrip icon in the Toolbox. (You’ll find the MenuStrip control in the
Menus & Toolbars tab of the Toolbox.) An instance of the MenuStrip control will be added to
the form, and a single menu command will appear on your form. Its caption will be Type Here.
If you don’t see the first menu item on the form right away, select the MenuStrip control in the
Components tray below the form. Do as the caption says: Click it and enter the first command’s
caption, File, as seen in Figure 7.17. To add items under the File menu, press Enter. To enter
another command in the main menu, press Tab. Depending on your action, another box will be
added, in which you can type the caption of the next command. Press Enter to move to the next
itemvertically, and Tab tomove to the next itemhorizontally. To insert a separator, enter a hyphen
(-) as the item’s caption.
When you hover the pointer over a menu item, a drop-down button appears to the right of
the item. Click this button to select the type of item you’ll place on the menu. This item can be a
MenuItem object, a separator, a ComboBox, or a TextBox. In this chapter, I’ll focus on menu items,
which are by far the most common elements on a menu. The last two options, however, allow you
to build elaborate menus, reminiscent of the Office menus.
Enter the items of the File menu — New, Open, Save, SaveAs,and Exit — and then click
somewhere on the form. All the temporary items (the ones with the Type Here caption) will
disappear, and the menu will be finalized on the form.
To add the Edit menu, select the MenuStrip icon to activate the visual menu editor and then
click the File item. In the new item that appears next to the File item on the control, enter the string
Edit. Press Enter and you’ll switch to the first item of the Edit menu. Fill the Edit menu with the
usual editing commands. Table 7.4 shows the captions (property Text) and names (property Name)
for each menu and each command. You can also insert a standard menu with the Insert Standard
Items command of the MenuStrip object’s context menu.Petroutsos c07.tex V3 - 01/28/2008 1:11pm Page 256
256 CHAPTER 7 WORKING WITH FORMS
Figure 7.17
Designing a menu on
the form
Table 7.4: The Captions and Names of the File and Edit Menus
Caption Name
File FileMenu
New FileNew
Open FileOpen
Save FileSave
Save As FileSaveAs
Exit FileExit
Edit EditMenu
Copy EditCopy
Cut EditCut
Paste EditPaste
The leftmost items in Table 7.4 are the names of the first-level menus (File and Edit); the
captions that are indented in the table are the commands on these two menus. Each menu item
has a name, which allows you to access its properties from within your code. The same name
is also used in naming the Click event handler of the item. The default names of the menu
items you add visually to the application’s menu are based on the item’s caption followed by the
suffix ToolStripMenuItem (FileToolStripMenuItem, NewToolStripMenuItem, and so on). You’llPetroutsos c07.tex V3 - 01/28/2008 1:11pm Page 257
DESIGNING MENUS 257
probably want to change the default names to something less redundant. To do so, change the
Name property in the Properties window. To view the properties of a menu item, right-click it and
select Properties from the context menu.
The most convenient method of editing a menu is to use the Items Collection Editor window,
which is shown in Figure 7.18. This isn’t a visual editor, but you can set all the properties of each
menu item without having to switch to the Properties window.
Figure 7.18
Editing a menu with the
Items Collection Editor
The Add button adds to the menu an item of the type specified in the combo box next to it (a
menu item, combo box, or text box). To insert an item at a different location, add it to the menu
and then use the arrow buttons to move it up or down. As you add new items, you can set their
Text and Name properties on the right pane of the editor. You can also set their font, set the align-
ment and orientation of the text, and specify an image to be displayed along with the text. To
add an image to a menu item, locate the Image property and click the ellipsis button. A dialog
box will appear, in which you can select the appropriate resource. Notice that all the images you
use on your form are stored as resources of the project. You can add all the images and icons you
might need in a project to the same resource file and reuse them at will. The TextImageRelation
property allows you to specify the relative positions of the text and the image. You can also select
to display text only, images only, or text and images for each menu item with the DisplayStyle
property.
If the menu item leads to a submenu, you must also specify the submenu’s items. Locate the
DropDownItems property and click the ellipsis button. An identical window will appear, in which
you can enter the drop-down items of the current menu item. Notice that the menu on the form is
continuously updated while you edit it in the Items Collection Editor window, so you can see the
effects of your changes on the form. Personally, I’m more productive with the editor than with the
visual tools, mainly because all the properties are right there, and I don’t have to switch between
the design surface and the Properties window.
The ToolStripMenuItem Properties
The ToolStripMenuItem class represents a menu command, at any level. If a command leads to
a submenu, it’s still represented by a ToolStripMenuItem object, which has its own collection ofPetroutsos c07.tex V3 - 01/28/2008 1:11pm Page 258
258 CHAPTER 7 WORKING WITH FORMS
ToolStripMenuItem objects: the DropDownItems collection, which is made up of ToolStripMenu-
Item objects. The ToolStripMenuItem class provides the following properties, which you can set
in the Properties window at design time or manipulate from within your code:
Checked Some menu commands act as toggles, and they are usually selected (checked) to
indicate that they are on, or are deselected (unchecked) to indicate that they are off. To initially
display a check mark next to a menu command, set its Checked property to True. You can also
access this property from within your code to change the checked status of a menu command
at runtime. For example, to toggle the status of a menu command called FntBold,usethis
statement:
FntBold.Checked = Not FntBold.Checked
Enabled Some menu commands aren’t always available. The Paste command, for example,
has no meaning if the Clipboard is empty (or if it contains data that can’t be pasted in the cur-
rent application). To indicate that a command can’t be used at the time, you set its Enabled
property to False. The command then appears grayed out in the menu, and although it can
be highlighted, it can’t be activated. The following statements enable and disable the Undo
command depending on whether the TextBox1 control can undo the most recent operation:
If TextBox1.CanUndo Then
cmdUndo.Enabled = True
Else
cmdUndo.Enabled = False
End If
cmdUndo is the name of the Undo command in the application’s Edit menu. The CanUndo prop-
erty of the TextBox control returns a True/False value that indicates whether the last action can
be undone or not.
IsOnDropDown If the menu command, represented by a ToolStripMenuItem object, belongs
to a submenu, its IsOnDropDown property is True; otherwise, it’s False. The IsOnDropDown
property is read-only and False for the items on the first level of the menu.
Visible To remove a command temporarily from the menu, set the command’s Visible
property to False. The Visible property isn’t used frequently in menu design. In general, you
should prefer to disable a command to indicate that it can’t be used at the time (some other
action is required to enable it). Making a command invisible frustrates users, who might spend
time trying to locate the command in another menu.
Programming Menu Commands
When a menu item is selected by the user, it triggers a Click event. To program a menu item,
insert the appropriate code in the item’s Click event handler. The Exit command’s code would be
something like the following:
Sub menuExit(...) Handles menuExit.Click
End
End SubPetroutsos c07.tex V3 - 01/28/2008 1:11pm Page 259
DESIGNING MENUS 259
If you need to execute any cleanup code before the application ends, place it in the CleanUp()
subroutine and call this subroutine from within the Exit item’s Click event handler:
Sub menuExit(...) Handles menuExit.Click
CleanUp()
End
End Sub
The same subroutine must also be called from within the FormClosing event handler of the
application’s main form because some users might terminate the application by clicking the form’s
Close button.
An application’s Open menu command contains the code that prompts the user to select a file
and then open it. You will see many examples of programming menu commands in the following
chapters. All you really need to know now is that each menu item is a ToolStripMenuItem object,
and it fires the Click event every time it’s selected with the mouse or the keyboard. In most cases,
you can treat the Click event handler of a ToolStripMenuItem object just like the Click event
handler of a Button.
Another interesting event of the ToolStripMenuItem is the DropDownOpened event, which is
fired when the user opens a menu or submenu (in effect, when the user clicks a menu item that
leads to a submenu). In this event’s handler, you can insert code to modify the submenu. The
Edit menu of just about any application contains the ubiquitous Cut/Copy/Paste commands.
These commands are not meaningful at all times. If the Clipboard doesn’t contain text, the Paste
command should be disabled. If no text is selected, the Copy and Cut commands should also be
disabled. Here’s how you could change the status of the Paste command from within the Drop-
DownOpened event handler of the Edit menu:
If My.Computer.Clipboard.ContainsText Then
PasteToolStripMenuItem.Enabled = True
Else
PasteToolStripMenuItem.Enabled = True
End If
Likewise, to change the status of the Cut and Copy commands, use the following statements in
the DropDownOpened event of the ToolStripMenuItem that represents the Edit menu:
If txtEditor.SelectedText.Trim.Length > 0 Then
CopyToolStripMenuItem.Enabled = True
CutToolStripMenuItem.Enabled = True
Else
CopyToolStripMenuItem.Enabled = False
CutToolStripMenuItem.Enabled = False
End If
Using Access and Shortcut Keys
Menus provide a convenient way to display a large number of choices to the user. They allow
you to organize commands in groups, according to their functions, and are available at all times.
Opening menus and selecting commands with the mouse, however, can be an inconvenience.
When using a word processor, for example, you don’t want to have to take your hands off the
keyboard and reach for the mouse. To simplify menu access, Windows forms support access keys
and shortcut keys.Petroutsos c07.tex V3 - 01/28/2008 1:11pm Page 260
260 CHAPTER 7 WORKING WITH FORMS
Access Keys
Access keys allow the user to open a menu by pressing the Alt key and a letter key. To open the Edit
menu in all Windows applications, for example, you can press Alt+E. E is the Edit menu’s access
key. After the menu is open, the user can select a command with the arrow keys or by pressing
another key, which is the command’s access key, without holding down the Alt key.
Access keys are designated by the designer of the application and aremarked with an underline
character. To assign an access key to a menu item, insert the ampersand symbol (&) in front of the
character you want to use as an access key in the ToolStripMenuItem’s Text property.
Default Access Keys Are Based on Item Captions
If you don’t designate access keys, Visual Basic will use the first character in each top-level menu as
its access key. The user won’t see the underline character under the first character, but can open the
menu by pressing the first character of its caption while holding down the Alt key. If two or more
menu captions begin with the same letter, the first (leftmost and topmost) menu will open.
Because the & symbol has a special meaning in menu design, you can’t use it in a menu
item’s caption. To actually display the & symbol in a caption, prefix it with another & symbol.
For example, the caption &Drag produces a command with the caption Drag (the first character
is underlined because it’s the access key). The caption Drag && Drop will create another com-
mand whose caption will be Drag & Drop. Finally, the string &Drag && Drop will create another
command with the caption Drag & Drop.
Shortcut Keys
Shortcut keys are similar to access keys, but instead of opening a menu, they run a command when
pressed. Assign shortcut keys to frequently used menu commands, so that users can reach them
with a single keystroke. Shortcut keys are combinations of the Ctrl key and a function or character
key. For example, the usual access key for the Close command (after the File menu is opened with
Alt+F) is C, but the usual shortcut key for the Close command is Ctrl+W.
Toassignashortcutkeytoamenucommand,dropdownthe ShortcutKeys list in the Tool-
StripMenuItem’s Properties window and select a keystroke. Specify a modifier (Shift, Ctrl, or Alt)
and a key. You don’t have to insert any special characters in the command’s caption, nor do you
have to enter the keystroke next to the caption. It will be displayed next to the command auto-
matically. When assigning access and shortcut keys, take into consideration the well-established
Windowsstandards.UsersexpectAlt+F to open the File menu, so don’t use Alt+F for the Format
menu. Likewise, pressing Ctrl+C universally performs the Copy command; don’t use Ctrl+Casa
shortcut for the Cut command.
Manipulating Menus at Runtime
Dynamic menus change at runtime to display more or fewer commands, depending on the current
status of the program. This section explores two techniques for implementing dynamic menus:
◆ Creating short and long versions of the same menu
◆ Adding and removing menu commands at runtimePetroutsos c07.tex V3 - 01/28/2008 1:11pm Page 261
DESIGNING MENUS 261
Creating Short and Long Menus
A common technique in menu design is to create long and short versions of a menu. If a menu
contains many commands, and most of the time only a few of them are needed, you can create
one menu with all the commands and another with the most common ones. The first menu is
the long one, and the second is the short one. The last command in the long menu should be Short
Menu, and when selected, it should display the short version. The last command in the shortmenu
should be Long Menu, and it should display the long version.
Figure 7.19 shows a long and a short version of the same menu from the LongMenu project.
The short version omits infrequently used commands and is easier to handle.
Figure 7.19
The two versions of the
Format menu of the
LongMenu application
To implement the LongMenu command, start a new project and create a menu with the options
shown in Figure 7.19. Listing 7.11 is the code that shows/hides the long menu in the MenuSize
command’s Click event.
Listing 7.11: TheMenuSizeMenu Item’s Click Event
Private Sub mnuSize Click(...) Handles mnuSize.Click
If mnuSize.Text = ”Short Menu” Then
mnuSize.Text = ”Long Menu”
Else
mnuSize.Text = ”Short Menu”
End If
mnuUnderline.Visible = Not mnuUnderline.Visible
mnuStrike.Visible = Not mnuStrike.Visible
mnuSmallCaps.Visible = Not mnuSmallCaps.Visible
mnuAllCaps.Visible = Not mnuAllCaps.Visible
End Sub
The subroutine in Listing 7.11 doesn’t do much. It simply toggles the Visible property of
certain menu commands and changes the command’s caption to Short Menu or Long Menu,
depending on the menu’s current status. Notice that because the Visible property is a True/False
value, we don’t care about its current status; we simply toggle the current status with the Not
operator.Petroutsos c07.tex V3 - 01/28/2008 1:11pm Page 262
262 CHAPTER 7 WORKING WITH FORMS
Adding and Removing Commands at Runtime
I conclude the discussion of menu design with a technique for building dynamic menus, which
grow and shrink at runtime. Many applications maintain a list of the most recently opened files in
the File menu. When you first start the application, this list is empty, and as you open and close
files, it starts to grow.
The RunTimeMenu project demonstrates how to add items to and remove items fromamenu at
runtime. The main menu of the application’s form contains the Run Time Menu submenu, which
is initially empty.
The two buttons on the form add commands to and remove commands from the Run Time
Menu. Each new command is appended at the end of the menu, and the commands are removed
from the bottom of the menu first (the most recently added commands are removed first). To
change this order and display the most recent command at the beginning of the menu, use the
Insert method instead of the Add method to insert the new item. Listing 7.12 shows the code
behind the two buttons that add and remove menu items.
Listing 7.12: Adding and RemovingMenu Items at Runtime
Private Sub bttnAddItem Click(...) Handles bttnAddItem.Click
Dim Item As New ToolStripMenuItem
Item.Text = ”Run Time Option” &
RunTimeMenuToolStripMenuItem.
DropDownItems.Count.ToString
RunTimeMenuToolStripMenuItem.DropDownItems.Add(Item)
AddHandler Item.Click,
New System.EventHandler(AddressOf OptionClick)
End Sub
Private Sub bttnRemoveItem Click(...) Handles bttnRemoveItem.Click
If RunTimeMenuToolStripMenuItem.DropDownItems.Count > 0 Then
Dim mItem As ToolStripItem
Dim items As Integer =
RunTimeMenuToolStripMenuItem.DropDownItems.Count
mItem = RunTimeMenuToolStripMenuItem.DropDownItems(items - 1)
RunTimeMenuToolStripMenuItem.DropDownItems.Remove(mItem)
End If
End Sub
TheRemovebutton’scodeusesthe Remove method to remove the last item in the menu by its
index, after making sure the menu contains at least one item. The Add button adds a new item,
sets its caption to Run Time Option n,where n is the item’s order in the menu. In addition, it
assigns an event handler to the new item’s Click event. This event handler is the same for all the
items added at runtime; it’s the OptionClick() subroutine.
All the runtime options invoke the same event handler — it would be quite cumbersome to
come up with a separate event handler for different items. In the single event handler, you can
examine the name of the ToolStripMenuItem object that invoked the event handler and act accord-
ingly. The OptionClick() subroutine used in Listing 7.13 displays the name of the menu item thatPetroutsos c07.tex V3 - 01/28/2008 1:11pm Page 263
DESIGNING MENUS 263
invoked it. It doesn’t do anything, but it shows you how to figure out which item of the Run Time
Menu was clicked.
Listing 7.13: Programming DynamicMenu Items
Private Sub OptionClick(...)
Dim itemClicked As New ToolStripMenuItem
itemClicked = CType(sender, ToolStripMenuItem)
MsgBox(”You have selected the item ” &
itemClicked.Text)
End Sub
Creating Context Menus
Nearly every Windows application provides a context menu that the user can invoke by
right-clicking a form or a control. (It’s sometimes called a shortcut menu or pop-up menu.) This
is a regular menu, but it’s not anchored on the form. It can be displayed anywhere on the form
or on specific controls. Different controls can have different context menus, depending on the
operations you can perform on them at the time.
To create a context menu, place a ContextMenuStrip control on your form. The new context
menu will appear on the form just like a regular menu, but it won’t be displayed there at run-
time. You can create as many context menus as you need by placing multiple instances of the
ContextMenuStrip control on your form and adding the appropriate commands to each one. To
associate a context menu with a control on your form, set the control’s ContextMenuStrip property
to the name of the corresponding context menu.
Designing a context menu is identical to designing a regular menu. The only difference is that
the first command in the menu is always ContextMenuStrip and it’s not displayed along with the
menu. Figure 7.20 shows a context menu at design time and how the same menu is displayed at
runtime.
You can create as many context menus as you want on a form. Each control has a ContextMenu
property, which you can set to any of the existing ContextMenuStrip controls. Select the control for
which you want to specify a context menu and locate the ContextMenu property in the Properties
window. Expand the drop-down list and select the name of the desired context menu.
To edit one of the context menus on a form, select the appropriate ContextMenuStrip control at
the bottom of the Designer. The corresponding context menu will appear on the form’s menu bar,
as if it were a regular form menu. This is temporary, however, and the only menu that appears on
the form’s menu bar at runtime is the one that corresponds to the MenuStrip control (and there
can be only one of them on each form).
Iterating a Menu’s Items
The last menu-related topic in this chapter demonstrates how to iterate through all the items of a
menu structure, including their submenus, at any depth. The main menu of an application can be
accessed by the expression Me.MenuStrip1 (assuming that you’re using the default names). This
is a reference to the top-level commands of the menu, which appear in the form’s menu bar. Each
command, in turn, is represented by a ToolStripMenuItem object. All the items under a menu
command form a ToolStripMenuItems collection, which you can scan to retrieve the individual
commands.Petroutsos c07.tex V3 - 01/28/2008 1:11pm Page 264
264 CHAPTER 7 WORKING WITH FORMS
Figure 7.20
Acontextmenuat
design time (top) and
at runtime (bottom)
The first command in a menu is accessed with the expression Me.MenuStrip1.Items(0);thisis
the File command in a typical application. The expression Me.MenuStrip1.Items(1) is the second
command on the same level as the File command (typically, the Edit menu).
To access the items under the first menu, use the DropDownItems collection of the top command.
The first command in the File menu can be accessed by this expression:
Me.MenuStrip1.Items(0).DropDownItems(0)
The same items can be accessed by name as well, and this is how you should manipulate the
menu items from within your code. In unusual situations, or if you’re using dynamic menus to
which you add and subtract commands at runtime, you’ll have to access the menu items through
the DropDownItems collection.
VB 2008 at Work: The MapMenu Project
The MapMenu project demonstrates how to access the items of a menu from within your applica-
tion’s code. The project’s main form contains a menu, a TextBox control, and a Button control that
prints the menu’s structure in the TextBox. You can edit the menu before running the program,
and the code behind the button will print the current structure of the menu items.Petroutsos c07.tex V3 - 01/28/2008 1:11pm Page 265
THE BOTTOM LINE 265
The code behind the Map Menu button iterates through the items of the form’s MenuStrip
items and prints their names, as well as the names of their drop-down items, in the Output win-
dow. It scans all the members of the control’s Items collection and prints their captions. After
printing each command’s caption, it calls the PrintSubMenu() subroutine, passing the current
ToolStripMenuItem as an argument. The PrintSubMenu() subroutine iterates through the Drop-
DownItems of the collection passed as an argument and prints their captions. If one of the items
leads to a nested submenu, it calls itself, passing the current ToolStripMenuItem as an argument.
You can open the MapMenu project with Visual Studio and examine its code.
The BottomLine
Use forms’ properties. Forms expose a lot of trivial properties for setting their appearance.
In addition, they expose a few properties that simplify the task of designing forms that can be
resized at runtime. The Anchor property causes a control to be anchored to one or more edges
of the form to which it belongs. The Dock property allows you to place on the form controls that
are docked to one of its edges. To create forms with multiple panes that the user can resize at
runtime, use the SplitContainer control. If you just can’t fit all the controls in a reasonably sized
form, use the AutoScroll properties to create a scrollable form.
Master It You’ve been asked to design a form with three distinct sections. You should also
allow users to resize each section. How will you design this form?
Design applications with multiple forms. Typical applications are made up of multiple
forms: the main form and one or more auxiliary forms. To show an auxiliary form from within
the main form’s code, call the auxiliary form’s Show method, or the ShowDialog method if you
want to display the auxiliary form modally (as a dialog box).
Master It How will you set the values of selected controls in a dialog box, display them,
and then read the values selected by the user from the dialog box?
Design dynamic forms. You can create dynamic forms by populating them with controls at
runtime through the form’s Controls collection. First, create instances of the appropriate con-
trols by declaring variables of the corresponding type. Then set the properties of the variable
that represents the control. Finally, place the control on the form by adding it to the form’s Con-
trols collection.
Master It How will you add a TextBox control to your form at runtime and assign a han-
dler to the control’s TextChanged event?
Design menus. Both form menus and context menus are implemented through the Menu-
Strip control. The items that make up the menu are ToolStripMenuItem objects. The ToolStrip-
MenuItem objects give you absolute control over the structure and appearance of the menus of
your application.
Master It What are the two basic events fired by the ToolStripMenuItem object?Petroutsos c07.tex V3 - 01/28/2008 1:11pm Page 266Petroutsos V1 c08.tex Page 267 01/28/2008 1:24pm
Chapter 8
More Windows Controls
In this chapter, we’ll continue our discussion of the basic Windows controls with the controls that
implement the common dialog boxes and the RichTextBox control.
The .NET Framework provides a set of controls for displaying common dialog boxes, such
as the Open or Color dialog boxes. Each of these controls encapsulates a large amount of
functionality that would take a lot of code to duplicate. The common dialog controls are
fundamental components because they enable you to design user interfaces with the look and feel
of a Windows application.
Besides the common dialog boxes, we’ll also explore the RichTextBox control, which is an
advanced version of the TextBox control. The RichTextBox control provides all the functionality
you’ll need to build a word processor — WordPad is actually built around the RichTextBox
control. The RichTextBox control allows you to format text bymixing fonts and attributes, aligning
paragraphs differently, and so on. You can also embed other objects in the document displayed in
a RichTextBox, such as images. Sure, the RichTextBox control is nothing like a full-fledged word
processor, but it’s a great tool for editing formatted text at runtime.
In this chapter you’ll learn how to do the following:
◆ Use the OpenFileDialog and SaveFileDialog controls to prompt users for filenames
◆ Use the ColorDialog and FontDialog controls to prompt users for colors and typefaces
◆ Use the RichTextBox control as an advanced text editor to present richly formatted text
The CommonDialog Controls
A rather tedious, but quite common, task in nearly every application is to prompt the user for
filenames, font names and sizes, or colors to be used by the application. Designing your own
dialog boxes for these purposes would be a hassle, not to mention that your applications wouldn’t
conform to the basic Windows interface design principles. In fact, all Windows applications use
standard dialog boxes for common operations; two of them are shown in Figure 8.1. These dialog
boxes are implemented as standard controls in the Toolbox. To use any of the common dialog
controls in your interface, just place the appropriate control from the Dialog section of the Toolbox
on your form and activate it from within your code by calling the ShowDialog method.
The common dialog controls are invisible at runtime, and they’re not placed on your forms,
because they’re implemented as modal dialog boxes and they’re displayed as needed. You simply
add them to the project by double-clicking their icons in the Toolbox; a new icon appears inPetroutsos V1 c08.tex Page 268 01/28/2008 1:24pm
268 CHAPTER 8 MORE WINDOWS CONTROLS
the components tray of the form, just below the Form Designer. The common dialog controls
in the Toolbox are the following:
OpenFileDialog Lets users select a file to open. It also allows the selection of multiple files
for applications that must process many files at once.
SaveFileDialog Lets users select or specify the path of a file in which the current document
will be saved.
FolderBrowserDialog Lets users select a folder (an operation that can’t be performed with
the OpenFileDialog control).
ColorDialog Lets users select a color from a list of predefined colors or specify custom colors.
FontDialog Lets users select a typeface and style to be applied to the current text selection.
The Font dialog box has an Apply button, which you can intercept from within your code and
use to apply the currently selected font to the text without closing the dialog box.
Figure 8.1
The Open and Font
common dialog boxes
There are three more common dialog controls: the PrintDialog, PrintPreviewDialog, and
PageSetupDialog controls. These controls are discussed in detail in Chapter 20, ‘‘Printing with
Visual Basic 2008,’’ in the context of VB’s printing capabilities.
Using the Common Dialog Controls
To display any of the common dialog boxes from within your application, you must first add an
instance of the appropriate control to your project. Then you must set some basic properties of
the control through the Properties window. Most applications set the control’s properties from
within the code because common dialogs interact closely with the application. When you call the
Color common dialog, for example, you should preselect a color from within your application
and make it the default selection on the control. When prompting the user for the color of the
text, the default selection should be the current setting of the control’s ForeColor property. Like-
wise, the Save dialog box must suggest a filename when it first pops up (or the file’s extension,
at least).Petroutsos V1 c08.tex Page 269 01/28/2008 1:24pm
THE COMMON DIALOG CONTROLS 269
To display a common dialog box from within your code, you simply call the control’s
ShowDialog method, which is common for all controls. Note that all common dialog controls
can be displayed only modally and they don’t expose a Show method. As soon as you call the
ShowDialog method, the corresponding dialog box appears onscreen, and the execution of the
program is suspended until the box is closed. Using the Open, Save, and FolderBrowser dialog
boxes, users can traverse the entire structure of their drives and locate the desired filename or
folder. When the user clicks the Open or Save button, the dialog box closes and the program’s
execution resumes. The code should read the name of the file selected by the user through the
FileName property and use it to open the file or store the current document there. The folder
selected in the FolderBrowserDialog control is returned to the application through the
SelectedPath property.
Here is the sequence of statements used to invoke the Open common dialog and retrieve the
selected filename:
If OpenFileDialog1.ShowDialog = Windows.Forms.DialogResult.OK Then
fileName = OpenFileDialog1.FileName
’ Statements to open the selected file
End If
The ShowDialog method returns a value indicating how the dialog box was closed. You should
read this value from within your code and ignore the settings of the dialog box if the operation
was cancelled.
The variable fileName in the preceding code segment is the full pathname of the file selected
by the user. You can also set the FileName property to a filename, which will be displayed when
the Open dialog box is first opened:
OpenFileDialog1.FileName =
”C:\WorkFiles\Documents\Document1.doc”
If OpenFileDialog1.ShowDialog =
Windows.Forms.DialogResult.OK Then
fileName = OpenFileDialog1.FileName
’ Statements to open the selected file
End If
Similarly, you can invoke the Color dialog box and read the value of the selected color by using
the following statements:
ColorDialog1.Color = TextBox1.BackColor
If ColorDialog1.ShowDialog = DialogResult.OK Then
TextBox1.BackColor = ColorDialog1.Color
End If
The ShowDialog method is common to all controls. The Title property is also common to all
controls and it’s the string displayed in the title bar of the dialog box. The default title is the name
of the dialog box (for example, Open, Color, and so on), but you can adjust it from within your code
with a statement such as the following:
ColorDialog1.Title = ”Select Drawing Color”Petroutsos V1 c08.tex Page 270 01/28/2008 1:24pm
270 CHAPTER 8 MORE WINDOWS CONTROLS
The ColorDialog Control
The Color dialog box, shown in Figure 8.2, is one of the simplest dialog boxes. Its Color property
returns the color selected by the user or sets the initially selected color when the user opens the
dialog box.
Figure 8.2
The Color dialog box
The following statements set the initial color of the ColorDialog control, display the dialog box,
and then use the color selected in the control to fill the form. First, place a ColorDialog control in
the form and then insert the following statements in a button’s Click event handler:
Private Sub Button1 Click(...)
Handles Button1.Click
ColorDialog1.Color = Me.BackColor
If ColorDialog1.ShowDialog =
Windows.Forms.DialogResult.OK Then
Me.BackColor = ColorDialog1.Color
End If
End Sub
The following sections discuss the basic properties of the ColorDialog control.
AllowFullOpen
Set this property to True if you want users to be able to open the dialog box and define their own
custom colors, like the one shown in Figure 8.2. The AllowFullOpen property doesn’t open the
custom section of the dialog box; it simply enables the Define Custom Colors button in the dialog
box. Otherwise, this button is disabled.
AnyColor
This property is a Boolean value that determines whether the dialog box displays all available
colors in the set of basic colors.Petroutsos V1 c08.tex Page 271 01/28/2008 1:24pm
THE COMMON DIALOG CONTROLS 271
Color
This is the color specified on the control. You can set it to a color value before showing the dialog
box to suggest a reasonable selection. On return, read the value of the same property to find out
which color was picked by the user in the control:
ColorDialog1.Color = Me.BackColor
If ColorDialog1.ShowDialog = DialogResult.OK Then
Me.BackColor = ColorDialog1.Color
End If
CustomColors
This property indicates the set of custom colors that will be shown in the dialog box. The Color
dialog box has a section called Custom Colors, in which you can display 16 additional custom
colors. The CustomColors property is an array of integers that represent colors. To display three
custom colors in the lower section of the Color dialog box, use a statement such as the following:
Dim colors() As Integer = {222663, 35453, 7888}
ColorDialog1.CustomColors = colors
You’d expect that the CustomColors property would be an array of Color values, but it’s not.
You can’t create the array CustomColors with a statement such as this one:
Dim colors() As Color =
{Color.Azure, Color.Navy, Color.Teal}
Because it’s awkward to work with numeric values, you should convert color values to integer
values by using a statement such as the following:
Color.Navy.ToArgb
The preceding statement returns an integer value that represents the color navy. This value,
however, is negative because the first byte in the color value represents the transparency of the
color. To get the value of the color, you must take the absolute value of the integer value returned
by the previous expression. To create an array of integers that represent color values, use a
statement such as the following:
Dim colors() As Integer =
{Math.Abs(Color.Gray.ToArgb),
Math.Abs(Color.Navy.ToArgb),
Math.Abs(Color.Teal.ToArgb)}
Now you can assign the colors array to the CustomColors property of the control, and the
colors will appear in the Custom Colors section of the Color dialog box.
SolidColorOnly
This indicates whether the dialog box will restrict users to selecting solid colors only. This
setting should be used with systems that can display only 256 colors. Although today few
systems can’t display more than 256 colors, some interfaces are limited to this number. WhenPetroutsos V1 c08.tex Page 272 01/28/2008 1:24pm
272 CHAPTER 8 MORE WINDOWS CONTROLS
you run an application through Remote Desktop, for example, only the solid colors are displayed
correctly on the remote screen, regardless of the remote computer’s graphics card (and that’s for
efficiency reasons).
The FontDialog Control
The Font dialog box, shown in Figure 8.3, lets the user review and select a font and then set its
size and style. Optionally, users can also select the font’s color and even apply the current set-
tings to the selected text on a control of the form without closing the dialog box, by clicking the
Apply button.
Figure 8.3
The Font dialog box
When the dialog is closed by clicking the OK button, you can retrieve the selected font by using
the control’s Font property. In addition to the OK button, the Font dialog box may contain the
Apply button, which reports the current setting to your application. You can intercept the Click
event of the Apply button and adjust the appearance of the text on your form while the common
dialog is still visible.
The main property of this control is the Font property, which sets the initially selected font in
the dialog box and retrieves the font selected by the user. The following statements display the
Font dialog box after setting the initial font to the current font of the TextBox1 control. When
the user closes the dialog box, the code retrieves the selected font and assigns it to the same
TextBox control:
FontDialog1.Font = TextBox1.Font
If FontDialog1.ShowDialog = DialogResult.OK Then
TextBox1.Font = FontDialog1.Font
End If
Use the following properties to customize the Font dialog box before displaying it.Petroutsos V1 c08.tex Page 273 01/28/2008 1:24pm
THE COMMON DIALOG CONTROLS 273
AllowScriptChange
This property is a Boolean value that indicates whether the Script combo box will be displayed in
the Font dialog box. This combo box allows the user to change the current character set and select
a non-Western language (such as Greek, Hebrew, Cyrillic, and so on).
AllowVerticalFonts
This property is a Boolean value that indicates whether the dialog box allows the display and
selection of both vertical and horizontal fonts. Its default value is False, which displays only
horizontal fonts.
Color, ShowColor
The Color property sets or returns the selected font color. To enable users to select a color for the
font, you must also set the ShowColor property to True.
FixedPitchOnly
This property is a Boolean value that indicates whether the dialog box allows only the selection
of fixed-pitch fonts. Its default value is False, which means that all fonts (fixed- and variable-pitch
fonts) are displayed in the Font dialog box. Fixed-pitch fonts, or monospaced fonts, consist of
characters of equal widths that are sometimes used to display columns of numeric values so that
the digits are aligned vertically.
Font
This property is a Font object. You can set it to the preselected font before displaying the dialog
box and assign it to a Font property upon return. You’ve already seen how to preselect a font and
how to apply the selected font to a control from within your application.
You can also create a new Font object and assign it to the control’s Font property. Upon return,
the TextBox control’s Font property is set to the selected font:
Dim newFont As Font(”Verdana”, 12, FontStyle.Underline)
FontDialog1.Font = newFont
If FontDialog1.ShowDialog() = DialogResult.OK Then
TextBox1.ForeColor = FontDialog1.Color
End If
FontMustExist
This property is a Boolean value that indicates whether the dialog box forces the selection of
an existing font. If the user enters a font name that doesn’t correspond to a name in the list
of available fonts, a warning is displayed. Its default value is True, and there’s no reason to
change it.
MaxSize, MinSize
These two properties are integers that determine the minimum and maximum point size the user
can specify in the Font dialog box. Use these two properties to prevent the selection of extremely
large or extremely small font sizes, because these fonts might throw off a well-balanced interface
(text will overflow in labels, for example).Petroutsos V1 c08.tex Page 274 01/28/2008 1:24pm
274 CHAPTER 8 MORE WINDOWS CONTROLS
ShowApply
This property is a Boolean value that indicates whether the dialog box provides an Apply button.
Its default value is False, so the Apply button isn’t normally displayed. If you set this property to
True, youmust also programthe control’s Apply event — the changes aren’t applied automatically
to any of the controls in the current form.
The following statements display the Font dialog box with the Apply button:
Private Sub Button2 Click(...) Handles Button2.Click
FontDialog1.Font = TextBox1.Font
FontDialog1.ShowApply = True
If FontDialog1.ShowDialog = DialogResult.OK Then
TextBox1.Font = FontDialog1.Font
End If
End Sub
The FontDialog control raises the Apply event every time the user clicks the Apply button. In
this event’s handler, you must read the currently selected font and use it in the form, so that users
can preview the effect of their selection:
Private Sub FontDialog1 Apply(...) Handles FontDialog1.Apply
TextBox1.Font = FontDialog1.Font
End Sub
ShowEffects
This property is a Boolean value that indicates whether the dialog box allows the selection of
special text effects, such as strikethrough and underline. The effects are returned to the
application as attributes of the selected Font object, and you don’t have to do anything special
in your application.
The OpenDialog and SaveDialog Controls
Open and Save As, the two most widely used common dialog boxes (see Figure 8.4), are
implemented by the OpenFileDialog and SaveFileDialog controls. Nearly every application
prompts users for filenames, and the .NET Framework provides two controls for this purpose.
The two dialog boxes are nearly identical, and most of their properties are common, so we’ll start
with the properties that are common to both controls.
When either of the two controls is displayed, it rarely displays all the files in any given folder.
Usually the files displayed are limited to the ones that the application recognizes so that users can
easily spot the file they want. The Filter property limits the types of files that will appear in the
Open or Save As dialog box.
It’s also standard for the Windows interface not to display the extensions of files (although
Windows distinguishes files by their extensions). The file type ComboBox, which appears at the
bottom of the form next to the File Name box, contains the various file types recognized by
the application. The various file types can be described in plain English with long descriptive
names and without their extensions.Petroutsos V1 c08.tex Page 275 01/28/2008 1:24pm
THE COMMON DIALOG CONTROLS 275
Figure 8.4
The Open and Save As
common dialog boxes
The extension of the default file type for the application is described by the DefaultExtension
property, and the list of the file types displayed in the Save As Type box is determined by the
Filter property.
To prompt the user for a file to be opened, use the following statements. The Open dialog box
displays the files with the extension .bin only.
OpenFileDialog1.DefaultExt = ”.bin”
OpenFileDialog1.AddExtension = True
OpenFileDialog1.Filter = ”Binary Files|*.bin”
If OpenFileDialog1.ShowDialog() =
Windows.Forms.DialogResult.OK Then
Debug.WriteLine(OpenFileDialog1.FileName)
End If
The following sections describe the properties of the OpenFileDialog and SaveFileDialog
controls.
AddExtension
This property is a Boolean value that determines whether the dialog box automatically adds an
extension to a filename if the user omits it. The extension added automatically is the one specifiedPetroutsos V1 c08.tex Page 276 01/28/2008 1:24pm
276 CHAPTER 8 MORE WINDOWS CONTROLS
by the DefaultExtension property, which you must set before calling the ShowDialog method.
This is the default extension of the files recognized by your application.
CheckFileExists
This property is a Boolean value that indicates whether the dialog box displays a warning if the
user enters the name of a file that does not exist in the Open dialog box, or if the user enters
the name of a file that exists in the Save dialog box.
CheckPathExists
This property is a Boolean value that indicates whether the dialog box displays a warning if the
user specifies a path that does not exist, as part of the user-supplied filename.
DefaultExt
This property sets the default extension for the filenames specified on the control. Use this prop-
erty to specify a default filename extension, such as .txt or .doc, so that when a file with no
extension is specified by the user, the default extension is automatically appended to the filename.
You must also set the AddExtension property to True. The default extension property starts with
the period, and it’s a string — for example, .bin.
DereferenceLinks
This property indicates whether the dialog box returns the location of the file referenced by the
shortcut or the location of the shortcut itself. If you attempt to select a shortcut on your desktop
when the DereferenceLinks property is set to False, the dialog box will return to your application
a value such as C:\WINDOWS\SYSTEM32\lnkstub.exe, which is the name of the shortcut, not the
name of the file represented by the shortcut. If you set the DereferenceLinks property to True,
the dialog box will return the actual filename represented by the shortcut, which you can use in
your code.
FileName
Use this property to retrieve the full path of the file selected by the user in the control. If you set
this property to a filename before opening the dialog box, this value will be the proposed filename.
The user can click OK to select this file or select another one in the control. The two controls
provide another related property, the FileNames property, which returns an array of filenames.
To find out how to allow the user to select multiple files, see the discussion of the MultipleFiles
and FileNames properties in ‘‘VB 2008 atWork: Multiple File Selection’’ at the end of
this section.
Filter
This property is used to specify the type(s) of files displayed in the dialog box. To display text files
only, set the Filter property to Text files|*.txt. The pipe symbol separates the description of
the files (what the user sees) from the actual extension (how the operating system distinguishes
the various file types).
If you want to display multiple extensions, such as .BMP, .GIF,and .JPG, use a semicolon to
separate extensions with the Filter property. Set the Filter property to the string Images|*.BMP;Petroutsos V1 c08.tex Page 277 01/28/2008 1:24pm
THE COMMON DIALOG CONTROLS 277
*.GIF;*.JPG to display all the files of these three types when the user selects Images in the Save As
Type combo box, under the box with the filename.
Don’t include spaces before or after the pipe symbol because these spaces will be displayed
on the dialog box. In the Open dialog box of an image-processing application, you’ll probably
provide options for each image file type, as well as an option for all images:
OpenFileDialog1.Filter =
”Bitmaps|*.BMP|GIF Images|*.GIF|”&
”JPEG Images|*.JPG|All Images|*.BMP;*.GIF;*.JPG”
FilterIndex
When you specify more than one file type when using the Filter property of the Open dialog
box, the first file type becomes the default. If you want to use a file type other than the first one,
use the FilterIndex property to determine which file type will be displayed as the default when
the Open dialog box is opened. The index of the first type is 1, and there’s no reason to ever set
this property to 1. If you use the Filter property value of the example in the preceding section
and set the FilterIndex property to 2, the Open dialog box will display GIF files by default.
InitialDirectory
This property sets the initial folder whose files are displayed the first time that the Open and Save
dialog boxes are opened. Use this property to display the files of the application’s folder or to spec-
ify a folder in which the application stores its files by default. If you don’t specify an initial folder,
the dialog box will default to the last folder where the most recent file was opened or saved. It’s
also customary to set the initial folder to the application’s path by using the following statement:
OpenFileDialog1.InitialDirectory = Application.ExecutablePath
The expression Application.ExecutablePath returns the path in which the application’s
executable file resides.
RestoreDirectory
Every time the Open and Save As dialog boxes are displayed, the current folder is the one that was
selected by the user the last time the control was displayed. The RestoreDirectory property is a
Boolean value that indicates whether the dialog box restores the current directory before closing.
Its default value is False, which means that the initial directory is not restored automatically.
The InitialDirectory property overrides the RestoreDirectory property.
The following four properties are properties of the OpenFileDialog control only: FileNames,
MultiSelect, ReadOnlyChecked,and ShowReadOnly.
FileNames
If the Open dialog box allows the selection of multiple files (see the later section ‘‘VB 2008 atWork:
Multiple File Selection’’), the FileNames property contains the pathnames of all selected files.
FileNames is a collection, and you can iterate through the filenames with an enumerator. This
property should be used only with the OpenFileDialog control, even though the SaveFileDialog
control exposes a FileNames property.Petroutsos V1 c08.tex Page 278 01/28/2008 1:24pm
278 CHAPTER 8 MORE WINDOWS CONTROLS
MultiSelect
This property is a Boolean value that indicates whether the user can select multiple files in the
dialog box. Its default value is False, and users can select a single file. When the MultiSelect
property is True, the user can select multiple files, but they must all come from the same folder
(you can’t allow the selection of multiple files from different folders). This property is unique to
the OpenFileDialog control.
ReadOnlyChecked, ShowReadOnly
The ReadOnlyChecked property is a Boolean value that indicates whether the Read-Only check
box is selected when the dialog box first pops up (the user can clear this box to open a file in
read/write mode). You can set this property to True only if the ShowReadOnly property is also set
to True. The ShowReadOnly property is also a Boolean value that indicates whether the Read-Only
check box is available. If this check box appears on the form, the user can select it so the file will
be opened as read-only. Files opened as read-only shouldn’t be saved onto the same file — always
prompt the user for a new filename.
The OpenFile and SaveFile Methods
The OpenFileDialog control exposes the OpenFile method, which allows you to quickly open the
selected file. Likewise, the SaveFileDialog control exposes the SaveFile method, which allows
you to quickly save a document to the selected file. Normally, after retrieving the name of the
file selected by the user, you must open this file for reading (in the case of the Open dialog box)
or writing (in the case of the Save dialog box). The topic of reading from or writing to files is
discussed in detail in Chapter 15, ‘‘Accessing Folders and Files.’’
When this method is applied to the Open dialog box, the file is opened with read-only permis-
sion. The same method can be applied to the SaveFile dialog box, in which case the file is opened
with read-write permission. Both methods return a Stream object, and you can call this object’s
Read and Write methods to read from or write to the file.
VB 2008 at Work: Multiple File Selection
The Open dialog box allows the selection of multiple files. This feature can come in handy when
you want to process files en masse. You can let the user select many files, usually of the same type,
and then process them one at a time. Or, you might want to prompt the user to select multiple files
to be moved or copied.
To allow the user to select multiple files in the Open dialog box, set the MultiSelect property
to True. The user can then select multiple files with the mouse by holding down the Shift or Ctrl
key. The names of the selected files are reported by the property FileNames, which is an array
of strings. The FileNames array contains the pathnames of all selected files, and you can iterate
through them and process each file individually.
One of this chapter’s sample projects is the MultipleFiles project, which demonstrates how to
use the FileNames property. The application’s form is shown in Figure 8.5. The button at the top
of the form displays the Open dialog box, where you can select multiple files. After closing the
dialog box by clicking the Open button, the application displays the pathnames of the selected
files on a ListBox control.Petroutsos V1 c08.tex Page 279 01/28/2008 1:24pm
THE COMMON DIALOG CONTROLS 279
Figure 8.5
The MultipleFiles
project lets the user
select multiple files in
the Open dialog box.
The code behind the Open Files button is shown in Listing 8.1. In this example, I used the
array’s enumerator to iterate through the elements of the FileNames array. You can use any of the
methods discussed in Chapter 2, ‘‘The Visual Basic 2008 Language,’’ to iterate through the array.
Listing 8.1: ProcessingMultiple Selected Files
Private Sub bttnFile Click(...)
Handles bttnFile.Click
OpenFileDialog1.Multiselect = True
OpenFileDialog1.ShowDialog()
Dim filesEnum As IEnumerator
ListBox1.Items.Clear()
filesEnum = OpenFileDialog1.FileNames.GetEnumerator()
While filesEnum.MoveNext
ListBox1.Items.Add(filesEnum.Current)
End While
End Sub
The FolderBrowserDialog Control
Sometimes we need to prompt users for a folder, rather than a filename. An application that
processes files in batch mode shouldn’t force users to select the files to be processed. Instead, it
should allow users to select a folder and process all files of a specific type in the folder (it couldPetroutsos V1 c08.tex Page 280 01/28/2008 1:24pm
280 CHAPTER 8 MORE WINDOWS CONTROLS
encrypt all text documents or resize all image files, for example). As elaborate as the File Open
dialog box might be, it doesn’t allow the selection of a folder. To prompt users for a folder’s path,
use the FolderBrowser dialog box, which is a very simple one; it’s shown in Figure 8.6 in the
section ‘‘VB 2008 at Work: Folder Browsing Demo Project.’’ The FolderBrowserDialog control
exposes a small number of properties, which are discussed next.
RootFolder
This property indicates the initial folder to be displayed when the dialog box is shown. It is not
necessarily a string; it can also be a member of the SpecialFolder enumeration. To see the
members of the enumeration, enter the following expression:
FolderBrowserDialog1.RootFolder =
As soon as you enter the equals sign, you will see the members of the enumeration. The most
common setting for this property is My Computer, which represents the target computer’s file
system. You can set the RootFolder property to a number of special folders (for example, Personal,
Desktop, ApplicationData, LocalApplicationData, and so on). You can also set this property to a
string with the desired folder’s pathname.
SelectedFolder
After the user closes the FolderBrowser dialog box by clicking the OK button, you can retrieve the
name of the selected folder with the SelectedFolder property, which is a string, and you can
use it with themethods of the System.IO namespace to access andmanipulate the selected folder’s
files and subfolders.
ShowNewFolderButton
This property determines whether the dialog box will contain a New button; its default value
is True. When users click the New button to create a new folder, the dialog box prompts them
for the new folder’s name, and creates a new folder with the specified name under the
selected folder.
VB 2008 at Work: Folder Browsing Demo Project
The FolderBrowser control is a trivial control, but I’m including a sample application to demon-
strate its use. The same application demonstrates how to retrieve the files and subfolders of the
selected folder and how to create a directory listing in a RichTextBox control, like the one shown in
Figure 8.6. The members of the System.IO namespace, which allow you to access and manipulate
files and folders from within your code, are discussed in detail in Chapter 15.
The FolderBrowser dialog box is set to display the entire file system of the target computer and
is invoked with the following statements:
FolderBrowserDialog1.RootFolder = Environment.SpecialFolder.MyComputer
FolderBrowserDialog1.ShowNewFolderButton = False
If FolderBrowserDialog1.ShowDialog = DialogResult.OK Then
’ process files in selected folder
End IfPetroutsos V1 c08.tex Page 281 01/28/2008 1:24pm
THE COMMON DIALOG CONTROLS 281
Figure 8.6
Selecting a folder via the
FolderBrowser
dialog box
As usual, we examine the value returned by the ShowDialog method of the control and we
proceed if the user has closed the dialog box by clicking the OK button. The code that iterates
through the selected folder’s files and subfolders, shown in Listing 8.2, is basically a demonstration
of some members of the System.IO namespace, but I’ll review it briefly here.
Listing 8.2: Scanning a Folder
Private Sub bttnSelectFiles Click(...)
Handles bttnSelectFiles.Click
FolderBrowserDialog1.RootFolder =
Environment.SpecialFolder.MyComputer
FolderBrowserDialog1.ShowNewFolderButton = False
If FolderBrowserDialog1.ShowDialog = Windows.Forms.DialogResult.OK Then
RichTextBox1.Clear()
’ Retrieve initial folder
Dim initialFolder As String =
FolderBrowserDialog1.SelectedPath
Dim InitialDir As New IO.DirectoryInfo(
FolderBrowserDialog1.SelectedPath)Petroutsos V1 c08.tex Page 282 01/28/2008 1:24pm
282 CHAPTER 8 MORE WINDOWS CONTROLS
’ and print its name w/o any indentation
PrintFolderName(InitialDir, ””)
’ and then print the files in the top folder
If InitialDir.GetFiles(”*.*”).Length = 0 Then
SwitchToItalics()
RichTextBox1.AppendText(
”folder contains no files” & vbCrLf)
SwitchToRegular()
Else
PrintFileNames(InitialDir, ””)
End If
Dim DI As IO.DirectoryInfo
’ Iterate through every subfolder and print it
For Each DI In InitialDir.GetDirectories
PrintDirectory(DI)
Next
End If
End Sub
The selected folder’s name is stored in the initialFolder variable and is passed as an
argument to the constructor of the DirectoryInfo class. The InitialDir variable represents the
specified folder. This object is passed to the PrintFolderName() subroutine, which prints
the folder’s name in bold. Then the code iterates through the same folder’s files and prints them
with the PrintFileNames() subroutine, which accepts as an argument the DirectoryInfo object
that represents the current folder and the indentation level. After printing the initial folder’s name
and the names of the files in the folder, the code iterates through the subfolders of the initial folder.
The GetDirectories method of the DirectoryInfo class returns a collection of objects, one for each
subfolder under the folder represented by the InitialDir variable. For each subfolder, it calls the
PrintDirectory() subroutine, which prints the folder’s name and the files in this folder,
and then iterates through the folder’s subfolders. The code that iterates through the selected
folder’s files and subfolders is shown in Listing 8.3.
Listing 8.3: The PrintDirectory() Subroutine
Private Sub PrintDirectory(ByVal CurrentDir As IO.DirectoryInfo)
Static IndentationLevel As Integer = 0
IndentationLevel += 1
Dim indentationString As String = ””
indentationString =
New String(Convert.ToChar(vbTab), IndentationLevel)
PrintFolderName(CurrentDir, indentationString)
If CurrentDir.GetFiles(”*.*”).Length = 0 Then
SwitchToItalics()
RichTextBox1.AppendText(indentationString &
”folder contains no files” & vbCrLf)
SwitchToRegular()Petroutsos V1 c08.tex Page 283 01/28/2008 1:24pm
THE RICHTEXTBOX CONTROL 283
Else
PrintFileNames(CurrentDir, indentationString)
End If
Dim folder As IO.DirectoryInfo
For Each folder In CurrentDir.GetDirectories
PrintDirectory(folder)
Next
IndentationLevel -= 1
End Sub
The code that iterates through the subfolders of a given folder is discussed in detail in
Chapter 15, so you need notworry if you can’t figure out howit works yet. In the following section,
you’ll learn how to display formatted text in the RichTextBox control.
The RichTextBox Control
The RichTextBox control is the core of a full-blown word processor. It provides all the functionality
of a TextBox control; it can handle multiple typefaces, sizes, and attributes, and offers precise
control over the margins of the text (see Figure 8.7). You can even place images in your text on a
RichTextBox control (although you won’t have the kind of control over the embedded images that
you have with Microsoft Word).
Figure 8.7
A word processor based
on the functionality of
the RichTextBox control
The fundamental property of the RichTextBox control is its Rtf property. Similar to the
Text property of the TextBox control, this property is the text displayed on the control. Unlike
the Text property, however, which returns (or sets) the text of the control but doesn’t contain
formatting information, the Rtf property returns the text along with any formatting informa-
tion. Therefore, you can use the RichTextBox control to specify the text’s formatting, including
paragraph indentation, font, and font size or style.Petroutsos V1 c08.tex Page 284 01/28/2008 1:24pm
284 CHAPTER 8 MORE WINDOWS CONTROLS
RTF, which stands for Rich Text Format, is a standard for storing formatting information along
with the text. The beauty of the RichTextBox control for programmers is that they don’t need
to supply the formatting codes. The control provides simple properties to change the font of
the selected text, change the alignment of the current paragraph, and so on. The RTF code is
generated internally by the control and used to save and load formatted files. It’s possible to
create elaborately formatted documents without knowing the RTF specification.
The WordPad application that comes with Windows is based on the RichTextBox control.
You can easily duplicate every bit of WordPad’s functionality with the RichTextBox control,
as you will see later, in the section ‘‘VB 2008 at Work: The RTFPad Project.’’
The RTF Language
A basic knowledge of the RTF format, its commands, and how it works will certainly help you
understand the RichTextBox control’s inner workings. RTF is a language that uses simple
commands to specify the formatting of a document. These commands, or tags,areASCIIstrings,
such as \par (the tag that marks the beginning of a new paragraph) and \b (the tag that turns
on the bold style). And this is where the value of the RTF format lies. RTF documents don’t
contain special characters and can be easily exchanged among different operating systems and
computers, as long as there is an RTF-capable application to read the document. Let’s look at an
RTF document in action.
Open the WordPad application (choose Start  Programs  Accessories  WordPad) and
enter a few lines of text (see Figure 8.8). Select a few words or sentences, and format them in
different ways with any of WordPad’s formatting commands. Then save the document in RTF
format: Choose File  Save As, select Rich Text Format, and then save the file as Document.rtf.
If you open this file with a text editor such as Notepad, you’ll see the actual RTF code that pro-
duced the document. A section of the RTF file for the document shown in Figure 8.8 is shown in
Listing 8.4.
Listing 8.4: The RTF Code for the First Paragraph of the Document in Figure 8.8
{\rtf1\ansi\ansicpg1252\deff0\deflang1033
{\fonttbl{\f0\fnil\fcharset0 Verdana;}{\f1\fswiss\fcharset0 Arial;}}
\viewkind4\uc1\pard\nowidctlpar\fi720 \b\f0\fs18 RTF
\b0 stands for \i Rich Text Format\i0 ,
which is a standard for storing formatting
information along with the text. The beauty
of the RichTextBox control for programmers
is that they don\rquote t need to supply the
formatting codes. The control provides simple
properties that turn the selected text into bold,
change the alignment of the current paragraph, and so on.\par
As you can see, all formatting tags are prefixed with the backslash (\) symbol. The tags
are shown in bold to stand out in the listing. To display the \ symbol itself, insert an additional
slash. Paragraphs are marked with the \par tag, and the entire document is enclosed in a pair
of curly brackets. The \li and \ri tags that are followed by a numeric value specify the amount
of the left and right indentation. If you assign this string to the RTF property of a RichTextBox
control, the result will be the document shown in Figure 8.7, formatted exactly as it appears
in WordPad.Petroutsos V1 c08.tex Page 285 01/28/2008 1:24pm
THE RICHTEXTBOX CONTROL 285
Figure 8.8
The formatting applied
to the text by using
WordPad’s commands
is stored along with the
text in RTF format.
RTF is similar to Hypertext Markup Language (HTML), and if you’re familiar with HTML,
a few comparisons between the two standards will provide helpful hints and insight into
the RTF language. Like HTML, RTF was designed to create formatted documents that could be
displayed on different systems. The following RTF segment displays a sentence with a few words
in italics:
\bRTF\b0 (which stands for Rich Text Format) is a \i
document formatting language\i0 that uses simple
commands to specify the formatting of the document.
The following is the equivalent HTML code:
<b>RTF</b> (which stands for Rich Text Format) is a
<i>document formatting language</i> that uses simple
commands to specify the formatting of the document.
The <b> and <i> tags of HTML, for example, are equivalent to the \b and \i tags of RTF.
The closing tags in RTF are \b0 and \i0, respectively.
RTF, however, is much more complicated than HTML. It’s not nearly as easy to understand
an RTF document as it is to understand an HTML document, because RTF was meant to be used
internally by applications. As you can see in Listing 8.3, RTF contains information about the font
being used, its size, and so on. Just as you need a browser to view HTML documents, you need an
RTF-capable application to view RTF documents. WordPad, for instance, supports RTF and can
both save a document in RTF format and read RTF files.
Although you don’t need to understand the RTF specifications to produce formatted text with
the RichTextBox control, if you want to generate RTF documents from within your code, visit the
RTF Cookbook site at http://search.cpan.org/˜sburke/RTF-Writer/lib/RTF/Cookbook.pod.Petroutsos V1 c08.tex Page 286 01/28/2008 1:24pm
286 CHAPTER 8 MORE WINDOWS CONTROLS
There’s also a Microsoft resource on RTF at http://msdn2.microsoft.com/en-us/library/
aa140277(office.10).aspx.
Text Manipulation and Formatting Properties
The RichTextBox control provides properties for manipulating the selected text on the control.
The names of these properties start with the Selection or Selected prefix, and the most
commonly used ones are shown in Table 8.1. Some of these properties are discussed in further
detail in following sections.
Table 8.1: RichTextBox Properties for Manipulating Selected Text
Property What It Manipulates
SelectedText The selected text
SelectedRtf The RTF code of the selected text
SelectionStart The position of the selected text’s first character
SelectionLength The length of the selected text
SelectionFont The font of the selected text
SelectionColor The color of the selected text
SelectionBackColor The background color of the selected text
SelectionAlignment The alignment of the selected text
SelectionIndent,
SelectionRightIndent,
SelectionHangingIndent
The indentation of the selected text
RightMargin The distance of the text’s right margin from the left edge of the control
SelectionTabs An array of integers that sets the tab stop positions in the control
SelectionBullet Whether the selected text is bulleted
BulletIndent The amount of bullet indent for the selected text
SelectedText
The SelectedText property represents the selected text, whether it was selected by the user via
the mouse or from within your code. To assign the selected text to a variable, use the following
statement:
selText=RichTextbox1.SelectedTextPetroutsos V1 c08.tex Page 287 01/28/2008 1:24pm
THE RICHTEXTBOX CONTROL 287
You can also modify the selected text by assigning a new value to the SelectedText property.
The following statement converts the selected text to uppercase:
RichTextbox1.SelectedText =
RichTextbox1.SelectedText.ToUpper
You can assign any string to the SelectedText property. If no text is selected at the time, the
statement will insert the string at the location of the pointer.
SelectionStart, SelectionLength
To simplify the manipulation and formatting of the text on the control, two additional properties,
SelectionStart and SelectionLength, report (or set) the position of the first selected character
in the text and the length of the selection, respectively, regardless of the formatting of the selected
text. One obvious use of these properties is to select (and highlight) some text on the control:
RichTextBox1.SelectionStart = 0
RichTextBox1.SelectionLength = 100
You can also use the Select method, which accepts as arguments the starting location and the
length of the text to be selected.
SelectionAlignment
Use this property to read or change the alignment of one or more paragraphs. This property’s
value is one of the members of the HorizontalAlignment enumeration: Left, Right, and Center.
Users don’t have to select an entire paragraph to align it; just placing the pointer anywhere in the
paragraph will do the trick, because you can’t align part of the paragraph.
SelectionIndent, SelectionRightIndent, SelectionHangingIndent
These properties allow you to change the margins of individual paragraphs. The Selection-
Indent property sets (or returns) the amount of the text’s indentation from the left edge of the
control. The SelectionRightIndent property sets (or returns) the amount of the text’s indentation
from the right edge of the control. The SelectionHangingIndent property indicates the inden-
tation of each paragraph’s first line with respect to the following lines of the same paragraph. All
three properties are expressed in pixels.
The SelectionHangingIndent property includes the current setting of the SelectionIndent
property. If all the lines of a paragraph are aligned to the left, the SelectionIndent property
can have any value (this is the distance of all lines from the left edge of the control), but the
SelectionHangingIndent property must be zero. If the first line of the paragraph is shorter
than the following lines, the SelectionHangingIndent has a negative value. Figure 8.9 shows
several differently formatted paragraphs. The settings of the SelectionIndent and Selection-
HangingIndent properties are determined by the two sliders at the top of the form.
SelectionBullet, BulletIndent
You use these properties to create a list of bulleted items. If you set the SelectionBullet property
to True, the selected paragraphs are formatted with a bullet style, similar to the <ul> tag in
HTML. To create a list of bulleted items, select them from within your code and assign the value
True to the SelectionBullet property. To change a list of bulleted items back to normal text,
make the same property False.Petroutsos V1 c08.tex Page 288 01/28/2008 1:24pm
288 CHAPTER 8 MORE WINDOWS CONTROLS
Figure 8.9
Various combinations
of the Selection-
Indent and
SelectionHanging-
Indent properties
produce all possible
paragraph styles.
The paragraphs formatted as bullets are also indented from the left by a small amount. To set
the amount of the indentation, use the BulletIndent property, which is also expressed in pixels.
SelectionTabs
Use this property to set the tab stops in the RichTextBox control. The Selection tab should be set to
an array of integer values, which are the absolute tab positions in pixels. Use this property to set
up a RichTextBox control for displaying tab-delimited data.
Using the RichTextBox Control to Display Delimited Data
As a developer I tend to favor the RichTextBox control over the TextBox control, even though I don’t
mix font styles or use the more-advanced features of the RichTextBox control. I suggest that you treat
the RichTextBox control as an enhanced TextBox control and use it as a substitute for the TextBox
control. One of the features of the RichTextBox control that I find very handy is its ability to set the
tab positions and display tabular data. You can also display tabular data on a ListView control, as you
will see in the following chapter, but it’s simpler to use a RichTextBox control with its ReadOnly
property set to True and its SelectionTabs property to an array of values that will accommodate
your data. Here’s how to set up a RichTextBox control to display a few rows of tab-delimited data:
RichTextBox1.ReadOnly = True
RichTextBox1.SelectionTabs = New Integer() {100, 160, 340}
RichTextBox1.AppendText(”R1C1” & vbTab &
”R1C2” & vbTab &
”R1C3” & vbCrLf)
RichTextBox1.AppendText(”R2C1” & vbTab &
”R2C2” & vbTab &
”R2C3” & vbCrLf)
This technique is a life-saver when I have to read the delimited data from a file. I just set up the tab
positions and then load the data with the LoadFile method, which is discussed in the following
section.Petroutsos V1 c08.tex Page 289 01/28/2008 1:24pm
THE RICHTEXTBOX CONTROL 289
Methods
The first two methods of the RichTextBox control you need to know are SaveFile and LoadFile.
The SaveFile method saves the contents of the control to a disk file, and the LoadFile method
loads the control from a disk file.
SaveFile
The syntax of the SaveFile method is as follows:
RichTextBox1.SaveFile(path, filetype)
where path is the path of the file in which the current document will be saved. By default, the
SaveFile method saves the document in RTF format and uses the .RTF extension. You can specify
a different format by using the second optional argument, which can take on the value of one of
the members of the RichTextBoxStreamType enumeration, described in Table 8.2.
Table 8.2: The RichTextBoxStreamType Enumeration
Format Effect
PlainText Stores the text on the control without any formatting
RichNoOLEObjs Stores the text without any formatting and ignores any embedded OLE objects
RichText Stores the text in RTF format (text with embedded RTF commands)
TextTextOLEObjs Stores the text along with the embedded OLE objects
UnicodePlainText Stores the text in Unicode format
LoadFile
Similarly, the LoadFile method loads a text or RTF file to the control. Its syntax is identical to the
syntax of the SaveFile method:
RichTextBox1.LoadFile(path, filetype)
The filetype argument is optional and can have one of the values of the RichTextBoxStream-
Type enumeration. Saving and loading files to and from disk files is as simple as presenting a Save
or Open common dialog to the user and then calling one of the SaveFile or LoadFile methods
with the filename returned by the common dialog box.
Select, SelectAll
The Select method selects a section of the text on the control, similar to setting the Selection-
Start and SelectionLength properties. The Select method accepts two arguments: the location
of the first character to be selected and the length of the selection:
RichTextBox1.Select(start, length)
The SelectAll method accepts no arguments and it selects all the text on the control.Petroutsos V1 c08.tex Page 290 01/28/2008 1:24pm
290 CHAPTER 8 MORE WINDOWS CONTROLS
Advanced Editing Features
The RichTextBox control provides all the text-editing features you’d expect to find in a text-editing
application, similar to the TextBox control. Among its more-advanced features, the RichTextBox
control provides the AutoWordSelection property, which controls how the control selects text. If
it’s True, the control selects a word at a time.
In addition to formatted text, the RichTextBox control can handle object linking and embedding
(OLE) objects. You can insert images in the text by pasting them with the Paste method. The
Paste method doesn’t require any arguments; it simply inserts the contents of the Clipboard at
the current location in the document.
The RichTextBox control encapsulates undo and redo operations at multiple levels. Each
operation has a name (Typing, Deletion, and so on), and you can retrieve the name of the
next operation to be undone or redone and display it on the menu. Instead of a simple Undo
or Redo caption, you can change the captions of the Edit menu to something like Undo Delete or
Redo Typing. To program undo and redo operations from within your code, you must use the
properties and methods discussed in the following sections.
CanUndo, CanRedo
These two properties are Boolean values you can read to find out whether there’s an operation that
can be undone or redone. If they’re False, you must disable the corresponding menu command
from within your code. The following statements disable the Undo command if there’s no action
to be undone at the time (EditUndo is the name of the Undo command on the Edit menu):
If RichTextBox1.CanUndo Then
EditUndo.Enabled = True
Else
EditUndo.Enabled = False
End If
These statements should appear in the menu item’s Select event handler (not in the Click
event handler) because they must be executed before the menu is displayed. The Select event is
triggered when a menu is opened. As a reminder, the Click event is fired when you click an item,
and not when you open a menu. For more information on programming the events of a menu, see
Chapter 7, ‘‘Working with Forms.’’
UndoActionName, RedoActionName
These two properties return the name of the action that can be undone or redone. The most
common value of both properties is Typing, which indicates that the Undo command will delete
a number of characters. Another common value is Delete, whereas some operations are named
Unknown. If you change the indentation of a paragraph on the control, this action’s name
is Unknown. Even when an action’s name is Unknown, the action can be undone with the Undo
method.
The following statement sets the caption of the Undo command to a string that indicates the
action to be undone (Editor is the name of a RichTextBox control):
If Editor.CanUndo Then
EditUndo.Text = ”Undo ” & Editor.UndoActionName
End IfPetroutsos V1 c08.tex Page 291 01/28/2008 1:24pm
THE RICHTEXTBOX CONTROL 291
Undo, Redo
These two methods undo or redo an action. The Undo method cancels the effects of the last action
of the user on the control. The Redo method redoes the most recent undo action. The Redo method
does not repeat the last action; it applies to undo operations only.
Cutting and Pasting
To cut, copy, and paste text in the RichTextBox control, you can use the same techniques you use
with the regular TextBox control. For example, you can replace the current selection by assigning
astringtothe SelectedText property. The RichTextBox, however, provides a few useful methods
for performing these operations. The Copy, Cut,and Paste methods perform the corresponding
operations. The Cut and Copy methods are straightforward and require no arguments. The Paste
method accepts a single argument, which is the format of the data to be pasted. Because the data
will come from the Clipboard, you can extract the format of the data in the Clipboard at the time
and then call the CanPaste method to find out whether the control can handle this type of data. If
so, you can then paste them in the control by using the Paste method.
This technique requires a bit of code because the Clipboard class doesn’t return the format of
the data in the Clipboard. You must call the following method of the Clipboard class to find out
whether the data is of a specific type and then paste it on the control:
If Clipboard.GetDataObject. –
GetDataPresent(DataFormats.Text) Then
RichTextBox.Paste(DataFormats.Text)
End If
This is a very simple case because we know that the RichTextBox control can accept text. For a
robust application, you must call the GetDataPresent method for each type of data your
application should be able to handle. (You may not want to allow users to paste all types of data
that the control can handle.) By the way, you can simplify the code with the help of the
ContainsText/ContainsImage and GetText/GetImage methods of the My.Application
.Clipboard object.
In the RTFPad project later in this chapter, we’ll use a structured exception handler to allow
users to paste anything in the control. If the control can’t handle it, the data won’t be pasted in
the control.
Searching in a RichTextBox Control
To locate a string in the text of the RichTextBox control, use the Find method. The Find method is
quite flexible, as it allows you to specify the type of the search, whether it will locate entire words,
and so on. The simplest form of this method accepts the search string as an argument and returns
the location of the first instance of the word in the text. If the search argument isn’t found, the
method returns the value −1.
RichTextBox1.Find(string)
Another equally simple syntax of the Find method allows you to specify how the control will
search for the string:
RichTextBox1.Find(string, searchMode)Petroutsos V1 c08.tex Page 292 01/28/2008 1:24pm
292 CHAPTER 8 MORE WINDOWS CONTROLS
The searchMode argument is a member of the RichTextBoxFinds enumeration, which is
shown in Table 8.3.
Table 8.3: The RichTextBoxFinds Enumeration
Value Effect
MatchCase Performs a case-sensitive search.
NoHighlight The text found will not be highlighted.
None Locates instances of the specified string even if they’re not whole words.
Reverse The search starts at the end of the document.
WholeWord Locates only instances of the specified string that are whole words.
Two more forms of the Find method allow you specify the range of the text in which the search
will take place:
RichTextBox1.Find(string, start, searchMode)
RichTextBox1.Find(string, start, end, searchMode)
The arguments start and end are the starting and ending locations of the search (use them to
search for a string within a specified range only). If you omit the end argument, the search will
start at the location specified by the start argument and will extend to the end of the text.
You can combine multiple values of the searchMode argument with the OR operator. The
default search is case-insensitive, covers the entire document, and highlights the matching text
on the control. The RTFPad application’s Find command demonstrates how to use the Find
method and its arguments to build a Search & Replace dialog box that performs all the types
of text-searching operations you might need in a text-editing application.
Handling URLs in the Document
An interesting feature of the RichTextBox control is the automatic formatting of URLs embedded
in the text. To enable this feature, set the DetectURLs property to True. Then, as soon as the control
determines that you’re entering a URL (usually after you enter the three w’s and the following
period), it will format the text as a hyperlink. When the pointer rests over a hyperlink, its shape
turns into a hand, just as it would in Internet Explorer. Run the RTFDemo project, enter a URL
such as http://www.sybex.com, and see how the RichTextBox control handles it.
In addition to formatting the URL, the RichTextBox control triggers the LinkClicked event
when a hyperlink is clicked. To display the corresponding page from within your code, enter the
following statement in the LinkClicked event handler:
Private Sub RichTextBox1 LinkClicked(
ByVal sender As Object,
ByVal e As System.Windows.Forms.LinkClickedEventArgs)
Handles RichTextBox1.LinkClicked
System.Diagnostics.Process.Start(e.LinkText)
End SubPetroutsos V1 c08.tex Page 293 01/28/2008 1:24pm
THE RICHTEXTBOX CONTROL 293
The System.Diagnostics.Process class provides the Start method, which starts an application.
You can specify either the name of the executable or the path of a file. If you specify, the Start
method will look up the associated application and start it. As you can see, handling embedded
URLs with the RichTextBox control is almost trivial.
Displaying a Formatted Directory Listing
This is a good point to review the subroutines that produced the formatted directory listings
shown in Figure 8.6. Folder names are printed in bold by the PrintFolderName() subroutine,
and filenames are printed in regular style by the PrintFileNames() subroutine. Both subroutines
accept as arguments a DirectoryInfo object that represents the folder whose name (or files) we
want to print, as well as an indentation string. This string is increased every time the code drills
down to a subfolder and is decreased every time it moves up to a parent folder. Listing 8.5 shows
the implementation of the two subroutines.
Listing 8.5: The PrintFolderName() and PrintFileNames() Subroutines
Private Sub PrintFolderName(
ByVal folder As IO.DirectoryInfo,
ByVal Indentation As String)
SwitchToBold()
RichTextBox1.AppendText(Indentation)
RichTextBox1.AppendText(folder.Name & vbCrLf)
SwitchToRegular()
End Sub
Private Sub PrintFileNames(
ByVal folder As IO.DirectoryInfo,
ByVal indentation As String)
Dim file As IO.FileInfo
For Each file In folder.GetFiles(”*.*”)
RichTextBox1.AppendText(
indentation & file.Name & vbCrLf)
Next
End Sub
The code for printing folder names and filenames is trivial. Before calling the AppendText
method to add a new folder name to the control, the code calls the SwitchToBold() subroutine.
After printing the folder name, it calls the SwitchToRegular subroutine to reset the font. The two
subroutines manipulate the SelectionFont property. Because no text is selected at the time, the
subroutines simply change the attributes of the text that will be appended to the control with
the next call to the AppendText method. The implementation of the two subroutines is shown next:
Private Sub SwitchToItalics()
RichTextBox1.SelectionFont =
New Font(RichTextBox1.SelectionFont.Name,
RichTextBox1.SelectionFont.Size, FontStyle.Italic)
End SubPetroutsos V1 c08.tex Page 294 01/28/2008 1:24pm
294 CHAPTER 8 MORE WINDOWS CONTROLS
Private Sub SwitchToRegular()
RichTextBox1.SelectionFont =
New Font(RichTextBox1.SelectionFont.Name,
RichTextBox1.SelectionFont.Size, FontStyle.Regular)
End Sub
VB 2008 at Work: The RTFPad Project
Creating a functional — even fancy — word processor based on the RichTextBox control is
unexpectedly simple. The challenge is to provide a convenient interface that lets the user select
text, apply attributes and styles to it, and then set the control’s properties accordingly. The RTFPad
sample application of this section does just that.
The RTFPad application (refer to Figure 8.7) is based on the TextPad application developed
in Chapter 6, ‘‘Basic Windows Controls.’’ It contains the same text-editing commands and some
additional text-formatting commands that can be implemented only with the RichTextBox control;
for example, it allows you to apply multiple fonts and styles to the text, and, of course, multiple
Undo/Redo operations.
The two TrackBar controls above the RichTextBox control manipulate the indentation of the
text.We already explored this arrangement in the discussion of the TrackBar control in Chapter 6,
but let’s review the operation of the two controls again. Each TrackBar control has a width of 816
pixels, which is equivalent to 8.5 inches on a monitor that has a resolution of 96 dots per inch (dpi).
The height of the TrackBar controls is 42 pixels, but unfortunately they can’t be made smaller. The
Minimum property of both controls is 0, and the Maximum property is 16. The TickFrequency is 1.
With these values, you can adjust the indentation in steps of 1 / 2 inch. Set the Maximum property to
32 and you’ll be able to adjust the indentation in steps of 1 / 4 inch. It’s not the perfect interface, as
it’s built for A4 pages in portrait orientation only. You can experiment with this interface to build
an even more functional word processor.
Each time the user slides the top TrackBar control, the code sets the SelectionIndent
property to the proper percentage of the control’s width. Because the SelectionHangingIndent
includes the value of the SelectionIndent property, it also adjusts the setting of the
SelectionHangingIndent property. Listing 8.6 is the code that’s executed when the upper
TrackBar control is scrolled.
Listing 8.6: Setting the SelectionIndent Property
Private Sub TrackBar1 Scroll(...)
Handles TrackBar1.Scroll
Editor.SelectionIndent = Convert.ToInt32(
Editor.Width *
(TrackBar1.Value / TrackBar1.Maximum))
Editor.SelectionHangingIndent = Convert.ToInt32(
Editor.Width *
(TrackBar2.Value / TrackBar2.Maximum) - Editor.SelectionIndent)
End Sub
Editor is the name of the RichTextBox control on the form. The code sets the control’s
indentation to the same percentage of the control’s width, as indicated by the value of the top
TrackBar control. It also does the same for the SelectionHangingIndent property, which isPetroutsos V1 c08.tex Page 295 01/28/2008 1:24pm
THE RICHTEXTBOX CONTROL 295
controlled by the lower TrackBar control. If the user has scrolled the lower TrackBar control, the
code sets the RichTextBox control’s SelectionHangingIndent property in the event handler, as
presented in Listing 8.7.
Listing 8.7: Setting the SelectionHangingIndent Property
Private Sub TrackBar2 Scroll(...)
Handles TrackBar2.Scroll
Editor.SelectionHangingIndent =
Convert.ToInt32(Editor.Width *
(TrackBar2.Value / TrackBar2.Maximum) -
Editor.SelectionIndent)
End Sub
Enter a few lines of text in the control, select one or more paragraphs, and check out the
operation of the two sliders.
The Scroll events of the two TrackBar controls adjust the text’s indentation. The opposite
action must take place when the user rests the pointer on another paragraph: The sliders’
positions must be adjusted to reflect the indentation of the selected paragraph. The selection of a
new paragraph is signaled to the application by the SelectionChanged event. The statements of
Listing 8.8, which are executed from within the SelectionChanged event, adjust the two slider
controls to reflect the indentation of the text.
Listing 8.8: Setting the Slider Controls
Private Sub Editor SelectionChanged(...)
Handles Editor.SelectionChanged
If Editor.SelectionIndent = Nothing Then
TrackBar1.Value = TrackBar1.Minimum
TrackBar2.Value = TrackBar2.Minimum
Else
TrackBar1.Value = Convert.ToInt32(
Editor.SelectionIndent *
TrackBar1.Maximum / Editor.Width)
TrackBar2.Value = Convert.ToInt32(
(Editor.SelectionHangingIndent /
Editor.Width) *
TrackBar2.Maximum + TrackBar1.Value)
End If
End Sub
If the user selects multiple paragraphs with different indentations, the SelectionIndent
property returns Nothing. The code examines the value of this property and, if it’s Nothing,
it moves both controls to the left edge. This way, the user can slide the controls and set the
indentations for multiple paragraphs. Some applications make the handles gray to indicate
that the selected text doesn’t have uniform indentation, but unfortunately you can’t gray thePetroutsos V1 c08.tex Page 296 01/28/2008 1:24pm
296 CHAPTER 8 MORE WINDOWS CONTROLS
sliders and keep them enabled. Of course, you can always design a custom control. This wouldn’t
be a bad idea, especially if you consider that the TrackBar controls are too tall for this type of
interface and can’t be made very narrow (as a result, the interface of the RTFPad application isn’t
very elegant).
The File Menu
The RTFPad application’s File menu contains the usual Open, Save, and Save As commands,
which are implemented with the control’s LoadFile and SaveFile methods. Listing 8.9 shows the
implementation of the Open command in the File menu.
Listing 8.9: The Open Command
Private Sub OpenToolStripMenuItem Click(...)
Handles OpenToolStripMenuItem.Click
If DiscardChanges() Then
OpenFileDialog1.Filter =
”RTF Files|*.RTF|DOC Files|*.DOC|”&
”Text Files|*.TXT|All Files|*.*”
If OpenFileDialog1.ShowDialog() =
DialogResult.OK Then
fName = OpenFileDialog1.FileName
Editor.LoadFile(fName)
Editor.Modified = False
End If
End If
End Sub
The fName variable is declared on the form’s level and holds the name of the currently open
file. This variable is set every time a new file is successfully opened and it’s used by the Save
command to automatically save the open file, without prompting the user for a filename.
DiscardChanges() is a function that returns a Boolean value, depending on whether the
control’s contents can be discarded. The function examines the Editor control’s Modified
property. If True, it prompts users as to whether they want to discard the edits. Depending
on the value of the Modified property and the user response, the function returns a Boolean
value. If the DiscardChanges() function returns True, the program goes on and opens a new doc-
ument. If the function returns False, the program aborts the operation to give the user a chance to
save the document. Listing 8.10 shows the DiscardChanges() function.
Listing 8.10: The DiscardChanges() Function
Function DiscardChanges() As Boolean
If Editor.Modified Then
Dim reply As MsgBoxResult
reply = MsgBox(
”Text hasn’t been saved. Discard changes?”,
MsgBoxStyle.YesNo)Petroutsos V1 c08.tex Page 297 01/28/2008 1:24pm
THE RICHTEXTBOX CONTROL 297
If reply = MsgBoxResult.No Then
Return False
Else
Return True
End If
Else
Return True
End If
End Function
The Modified property becomes True after typing the first character and isn’t reset back to
False. The RichTextBox control doesn’t handle this property very intelligently and doesn’t reset it
to False even after saving the control’s contents to a file. The application’s code sets the
Editor.Modified property to False after creating a new document, as well as after saving the
current document.
The Save As command (see Listing 8.11) prompts the user for a filename and then stores the
Editor control’s contents to the specified file. It also sets the fName variable to the file’s path, so
that the Save command can use it.
Listing 8.11: TheSaveAsCommand
Private Sub SaveAsToolStripMenuItem Click(...)
Handles SaveAsToolStripMenuItem.Click
SaveFileDialog1.Filter =
”RTF Files|*.RTF|DOC Files” &
”|*.DOC|Text Files|*.TXT|All Files|*.*”
SaveFileDialog1.DefaultExt = ”RTF”
If SaveFileDialog1.ShowDialog() = DialogResult.OK Then
fName = SaveFileDialog1.FileName
Editor.SaveFile(fName)
Editor.Modified = False
End If
End Sub
The Save command’s code is similar, only it doesn’t prompt the user for a filename. It calls the
SaveFile method, passing the fName variable as an argument. If the fName variable has no value
(in other words, if a user attempts to save a new document by using the Save command), the
code activates the event handler of the Save As command automatically and resets the control’s
Modified property to False. Listing 8.12 shows the code behind the Save command.
Listing 8.12: TheSaveCommand
Private Sub SaveToolStripMenuItem Click(...)
Handles SaveToolStripMenuItem.Click
If fName <> ”” Then
Editor.SaveFile(fName)
Editor.Modified = FalsePetroutsos V1 c08.tex Page 298 01/28/2008 1:24pm
298 CHAPTER 8 MORE WINDOWS CONTROLS
Else
SaveAsToolStripMenuItem Click(sender, e)
End If
End Sub
The Edit Menu
The Edit menu contains the usual commands for exchanging data through the Clipboard (Copy,
Cut, Paste), Undo/Redo commands, and a Find command to invoke the Search & Replace dialog
box. All the commands are almost trivial, thanks to the functionality built into the control. The
basic Cut, Copy, and Paste commands call the RichTextBox control’s Copy, Cut,and Paste
methods to exchange data through the Clipboard. Listing 8.13 shows the implementation of the
Paste command.
Listing 8.13: The Paste Command
Private Sub PasteToolStripMenuItem Click(...)
Handles PasteToolStripMenuItem.Click
Try
Editor.Paste()
Catch exc As Exception
MsgBox(
”Can’t paste current clipboard’s contents”)
End Try
End Sub
As you may recall from the discussion of the Paste command, we can’t use the CanPaste
method because it’s not trivial; you have to handle each data type differently. By using an
exception handler, we allow the user to paste all types of data that the RichTextBox control can
accept, and display a message when an error occurs.
The Undo and Redo commands of the Edit menu are coded as follows. First, we display the
name of the action to be undone or redone in the Edit menu. When the Edit menu is selected,
the DropDownOpened event is fired. This event takes place before the Click event, so I inserted a
few lines of code that read the name of the most recent action that can be undone or redone and
print it next to the Undo or Redo command’s caption. If there’s no such action, the program will
disable the corresponding command. Listing 8.14 is the code that’s executed when the Edit menu
is dropped.
Listing 8.14: Setting the Captions of the Undo and Redo Commands
Private Sub EditToolStripMenuItem DropDownOpened(...)
Handles EditToolStripMenuItem.DropDownOpened
If Editor.UndoActionName <> ”” Then
UndoToolStripMenuItem.Text =
”Undo ” & Editor.UndoActionName
UndoToolStripMenuItem.Enabled = TruePetroutsos V1 c08.tex Page 299 01/28/2008 1:24pm
THE RICHTEXTBOX CONTROL 299
Else
UndoToolStripMenuItem.Text = ”Undo”
UndoToolStripMenuItem.Enabled = False
End If
If Editor.RedoActionName <> ”” Then
RedoToolStripMenuItem.Text =
”Redo” & Editor.RedoActionName
RedoToolStripMenuItem.Enabled = True
Else
RedoToolStripMenuItem.Text = ”Redo”
RedoToolStripMenuItem.Enabled = False
End If
End Sub
When the user selects one of the Undo or Redo commands, the code simply calls the
appropriate method from within the menu item’s Click event handler, as shown in Listing 8.15.
Listing 8.15: Undoing and Redoing Actions
Private Sub RedoToolStripMenuItem Click(...)
Handles RedoToolStripMenuItem.Click
If Editor.CanRedo Then Editor().Redo()
End Sub
Private Sub UndoToolStripMenuItem Click(...)
Handles UndoToolStripMenuItem.Click
If Editor.CanUndo Then Editor.Undo()
End Sub
Calling the CanUndo and CanRedo method is unnecessary; if the corresponding action can’t be
performed, the two menu items will be disabled, but an additional check does no harm.
The Format Menu
The commands of the Format menu control the alignment and the font attributes of the current
selection. The Font command displays the Font dialog box and then assigns the font selected by
the user to the current selection. Listing 8.16 shows the code behind the Font command.
Listing 8.16: The Font Command
Private Sub FontToolStripMenuItem Click(...)
Handles FontToolStripMenuItem.Click
If Not Editor.SelectionFont Is Nothing Then
FontDialog1.Font = Editor.SelectionFont
ElsePetroutsos V1 c08.tex Page 300 01/28/2008 1:24pm
300 CHAPTER 8 MORE WINDOWS CONTROLS
FontDialog1.Font = Nothing
End If
FontDialog1.ShowApply = True
If FontDialog1.ShowDialog() = DialogResult.OK Then
Editor.SelectionFont = FontDialog1.Font
End If
End Sub
Notice that the code preselects a font in the dialog box, which is the font of the current selection.
If the current selection isn’t formatted with a single font, no font is preselected.
To enable the Apply button of the Font dialog box, set the control’s ShowApply property to True
and insert the following statement in its Apply event handler:
Private Sub FontDialog1 Apply(
ByVal sender As Object,
ByVal e As System.EventArgs)
Handles FontDialog1.Apply
Editor.SelectionFont = FontDialog1.Font
End Sub
The options of the Align menu set the RichTextBox control’s SelectionAlignment property
to different members of the HorizontalAlignment enumeration. The Align  Left command, for
example, is implemented with the following statement:
Editor.SelectionAlignment = HorizontalAlignment.Left
The Search & Replace Dialog Box
The Find command in the Edit menu opens the dialog box shown in Figure 8.10, which per-
forms search-and-replace operations (whole-word or case-sensitive match, or both). The Search &
Replace form (it’s the frmFind form in the project) has its TopMost property set to True, so that
it remains visible while it’s open, even if it doesn’t have the focus. The code behind the buttons
on this form is quite similar to the code for the Search & Replace dialog box of the TextPad appli-
cation, with one basic difference: the RTFPad project’s code uses the RichTextBox control’s Find
method; the simple TextBox control doesn’t provide an equivalent method and we had to use the
methods of the String class to perform the same operations. The Find method of the RichTextBox
control performs all types of searches, and some of its options are not available with the IndexOf
method of the String class.
To invoke the Search & Replace dialog box, the code calls the Show method of the frmFind
form, as discussed in Chapter 6, via the following statement:
frmFind.Show()
The Findmethod of the RichTextBox control allows you to performcase-sensitive or -insensitive
searches, as well as search for whole words only. These options are specified through an argument
of the RichTextBoxFinds type. The SetSearchMode() function (see Listing 8.17) examines the set-
tings of the two check boxes at the bottom of the form and sets the Find method’s search mode.Petroutsos V1 c08.tex Page 301 01/28/2008 1:24pm
THE RICHTEXTBOX CONTROL 301
Figure 8.10
The Search & Replace
dialog box of the RTFPad
application
Listing 8.17: Setting the Search Options
Function SetSearchMode() As RichTextBoxFinds
Dim mode As RichTextBoxFinds =
RichTextBoxFinds.None
If chkCase.Checked = True Then
mode = mode Or RichTextBoxFinds.MatchCase
End If
If chkWord.Checked = True Then
mode = mode Or RichTextBoxFinds.WholeWord
End If
Return mode
End Function
The Click event handlers of the Find and Find Next buttons call this function to retrieve the
constant that determines the type of search specified by the user on the form. This value is then
passed to the Find method. Listing 8.18 shows the code behind the Find and Find Next buttons.
Listing 8.18: The Find and Find Next Commands
Private Sub bttnFind Click(...)
Handles bttnFind.Click
Dim wordAt As Integer
Dim srchMode As RichTextBoxFinds
srchMode = SetSearchMode()
wordAt = frmEditor.Editor.Find(
txtSearchWord.Text, 0, srchMode)
If wordAt = -1 Then
MsgBox(”Can’t find word”)
Exit SubPetroutsos V1 c08.tex Page 302 01/28/2008 1:24pm
302 CHAPTER 8 MORE WINDOWS CONTROLS
End If
frmEditor.Editor.Select(wordAt,
txtSearchWord.Text.Length)
bttnFindNext.Enabled = True
bttnReplace.Enabled = True
bttnReplaceAll.Enabled = True
frmEditor.Editor.ScrollToCaret()
End Sub
Private Sub bttnFindNext Click(...)
Handles bttnFindNext.Click
Dim selStart As Integer
Dim srchMode As CompareMethod
srchMode = SetSearchMode()
selStart = frmEditor.Editor.Find(
txtSearchWord.Text,
frmEditor.Editor.SelectionStart + 2,
srchMode)
If selStart = -1 Then
MsgBox(”No more matches”)
Exit Sub
End If
frmEditor.Editor.Select(
selStart, txtSearchWord.Text.Length)
frmEditor.Editor.ScrollToCaret()
End Sub
Notice that both event handlers call the ScrollToCaret method to force the selected text to
become visible — should the Find method locate the desired string outside the visible segment
of the text.
The BottomLine
Use the OpenFileDialog and SaveFileDialog controls to prompt users for filenames.
Windows applications use certain controls to prompt users for common information, such as
filenames, colors, and fonts. Visual Studio provides a set of controls, which are grouped in the
Dialogs section of the Toolbox. All common dialog controls provide a ShowDialog method,
which displays the corresponding dialog box in a modal way. The ShowDialog method returns
a value of the DialogResult type, which indicates how the dialog box was closed, and you
should examine this value before processing the data.
Master It Your application needs to open an existing file. How will you prompt users for
the file’s name?
Master It You’re developing an application that encrypts multiple files (or resizes many
images) in batch mode. How will you prompt the user for the files to be processed?
Use the ColorDialog and FontDialog controls to prompt users for colors and typefaces.
The Color and Font dialog boxes allow you to prompt users for a color value and a font,Petroutsos V1 c08.tex Page 303 01/28/2008 1:24pm
THE BOTTOM LINE 303
respectively. Before showing the corresponding dialog box, set its Color or Font property
according to the current selection, and then call the control’s ShowDialog method.
Master It How will you display color attributes in the Color dialog box when you open it?
How will you display the attributes of the selected text’s font in the Font dialog box when
you open it?
Use the RichTextBox control as an advanced text editor to present richly formatted text.
The RichTextBox control is an enhanced TextBox control that can display multiple fonts and
styles, format paragraphs with different styles, and provide a few more advanced text-editing
features. Even if you don’t need the formatting features of this control, you can use it as an
alternative to the TextBox control. At the very least, the RichTextBox control provides more
editing features, a more-useful undo function, and more-flexible search features.
Master It You want to display a document with a title in large, bold type, followed by a
couple of items in regular style. How will you create a document like the following one on
a RichTextBox control?
Document’s Title
    Item 1
         Description for item 1
    Item 2
         Description for item 2Petroutsos V1 c08.tex Page 304 01/28/2008 1:24pmPetroutsos c09.tex V2 - 01/28/2008 1:28pm Page 305
Chapter 9
The TreeView and ListView
Controls
In Chapter 6, ‘‘Basic Windows Controls,’’ you learned how to use the ListBox control for
displaying lists of strings and storing objects. The items of a ListBox control can be sorted, but
they have no particular structure. I’m sure most of you wish that the ListBox control had more
‘‘features,’’ such as the means to store additional information along with each item or to present
hierarchical lists. A hierarchical list is a tree that reflects the structure of the list: items that belong
to other items appear under their parent with the proper indentation. For instance, a list of city
and state names should be structured so that each city appears under the corresponding state.
The answer to the shortcomings of the ListBox control can be found in the TreeView and
ListView controls. These twoWindows controls are among the more-advanced ones, and they are
certainly more difficult to program than the ones discussed in the preceding chapters. These two
controls, however, are the basic makings of unique user interfaces, as you’ll see in this chapter’s
examples. The TreeView and ListView controls implement two of the more-advanced data struc-
tures and were designed to hide much of the complexity of these structures — and they do this
very well.
In this chapter, you’ll learn how to do the following:
◆ Create and present hierarchical lists by using the TreeView control
◆ Create and present lists of structured items by using the ListView control
Understanding the ListView, TreeView, and ImageList
Controls
I will start with a general discussion of the two controls to help you understand what they do and
when to use them. A basic understanding of the data structures they implement is also required
to use them efficiently in your applications. Then I’ll discuss their members and demonstrate
how to use the controls. If you find the examples too difficult to understand, you can always
postpone the use of these controls in your applications.
Some of the code I present in this chapter can be used as is in many situations, so you should
look at the examples and see whether you can incorporate some of their code in your applications.
The ListView and TreeView controls are excellent tools for designing elaborate Windows inter-
faces, and I feel they deserve to be covered in detail. It’s also common to use the ImageList control
in conjunction with the ListView and TreeView controls. The purpose of the ImageList control is
to store the images that we want to display, along with the items of the other two controls, so I’ll
discuss briefly the ImageList control in this chapter.Petroutsos c09.tex V2 - 01/28/2008 1:28pm Page 306
306 CHAPTER 9 THE TREEVIEW AND LISTVIEW CONTROLS
Figure 9.1 shows the TreeView and ListView controls used in tandem. What you see in
Figure 9.1 is Windows Explorer, a utility for examining and navigating your hard disk’s struc-
ture. The left pane, where the folders are displayed, is a TreeView control. The folder names are
displayed in a manner that reflects their structure on the hard disk. You can expand and contract
certain branches and view only the segment(s) of the tree structure you’re interested in.
Figure 9.1
Windows Explorer is
made up of a Tree-
View (left pane) and
a ListView (right pane)
control.
The right pane is a ListView control. The items on the ListView control can be displayed in
five ways (as large or small icons, as a list, on a grid, or tiled). They are the various views you
can set through the View menu of Windows Explorer. Although most people prefer to look at the
contents of the folders as icons, the most common view is the Details view, which displays not
only filenames, but also their attributes. In the Details view, the list can be sorted according to any
of its columns, making it easy for the user to locate any item based on various criteria (file type,
size, creation date, and so on). A Windows Explorer window with a detailed view of the files is
shown later in this chapter, in Figure 9.4.
Tree and List Structures
The TreeView control implements a data structure known as a tree. A tree is the most appropriate
structure for storing hierarchical information. The organizational chart of a company, for example,
is a tree structure. Every person reports to another person above him or her, all the way to the
president or CEO. Figure 9.2 depicts a possible organization of continents, countries, and cities
as a tree. Every city belongs to a country, and every country to a continent. In the same way,
every computer file belongs to a folder that may belong to an even bigger folder, and so on up to
the drive level. You can’t draw large tree structures on paper, but it’s possible to create a similar
structure in the computer’s memory without size limitations.
Each item in the tree of Figure 9.2 is called a node, and nodes can be nested to any level. Oddly,
the top node is the root of the tree, and the subordinate nodes are called child nodes.Ifyoutryto
visualize this structure as a real tree, think of it as an upside-down treewith the branches emerging
from the root. The end nodes, which don’t lead to any other nodes, are called leaf nodes or
end nodes.Petroutsos c09.tex V2 - 01/28/2008 1:28pm Page 307
UNDERSTANDING THE LISTVIEW, TREEVIEW, AND IMAGELIST CONTROLS 307
Figure 9.2
The world viewed as a
tree
Berlin Munich Frankfurt
Germany France Spain
Africa Asia Europe S. America 1st level nodes
2nd level nodes
3rd level nodes
Globe Root node



To locate a city, you must start at the root node and select the continent to which the city
belongs. Then you must find the country (in the selected continent) to which the city belongs.
Finally, you can find the city you’re looking for. If it’s not under the appropriate country node, it
doesn’t exist.
TreeView Items Are Just Strings
The items displayed on a TreeView control are just strings. Moreover, the TreeView control doesn’t
require that the items be unique. You can have identically named nodes in the same branch — as
unlikely as this might be for a real application. There’s no property that makes a node unique in the
tree structure or even in its own branch.
You can also start with a city and find its country. The country node is the city node’s parent
node. Notice that there is only one route from child nodes to their parent nodes, which means that
you can instantly locate the country or continent of a city. The data of Figure 9.2 is shown in Figure
9.3 in a TreeView control. Only the nodes we’re interested in are expanded. The plus sign indicates
that the corresponding node contains child nodes. To view them, click the button with the plus
sign to expand the node.
The tree structure is ideal for data with parent-child relations (relations that can be described as
belongs to or owns). The continents-countries-cities data is a typical example. The folder structure
on a hard disk is another typical example. Any given folder is the child of another folder or the
root folder.
Many programs are based on tree structures. Computerized board games use a tree structure
to store all possible positions. Every time the computer has to make a move, it locates the board’s
status on the tree and selects the ‘‘best’’ next move. For instance, in tic-tac-toe, the tree structure
that represents the moves in the game has nine nodes on the first level, which correspond to all the
possible positions for the first token on the board (the X or O mark). Under each possible initial
position, there are eight nodes, which correspond to all the possible positions of the second token
on the board (one of the nine positions is already taken). On the second level, there are 9 × 8,
or 72, nodes. On the third level, there are 7 child nodes under each node that correspond to all
the possible positions of the third token, a total of 72 × 7, or 504 nodes, and so on. In each node,
you can store a value that indicates whether the corresponding move is good or bad. When the
computer has to make a move, it traverses the tree to locate the current status of the board, and
then it makes a good move.
Of course, tic-tac-toe is a simple game. In principle, you could design a chess game by using a
tree. This tree, however,would growso large so quickly that it couldn’t be stored in any reasonablePetroutsos c09.tex V2 - 01/28/2008 1:28pm Page 308
308 CHAPTER 9 THE TREEVIEW AND LISTVIEW CONTROLS
amount of memory. Moreover, scanning the nodes of this enormous tree would be an extremely
slow process. If you also consider that chess moves aren’t just good or bad (there are better and
not-so-good moves), and you must look ahead many moves to decide which move is the best
for the current status of the board, you’ll realize that this ad hoc approach is totally infeasible.
Practically speaking, such a program requires either infinite resources or infinite time. That’s why
the chess-playing algorithms use heuristic approaches, which store every recorded chess game in a
database and consult this database to pick the best next move.
Figure 9.3
The tree of Figure 9.2
implemented with a
TreeView control
Maintaining a tree structure is a fundamental operation in software design; computer science
students spend a good deal of their time implementing tree structures. Fortunately, with Visual
Basic you don’t have to implement tree structures on your own. The TreeView control is a mech-
anism for storing hierarchically structured data in a control with a visible interface. The TreeView
control hides (or encapsulates, in object-oriented terminology) the details of the implementation
and allows you to set up tree structures with a few lines of code — in short, all the gain without
the pain (almost).
The ListView control implements a simpler structure, known as a list. A list’s items aren’t
structured in a hierarchy; they are all on the same level and can be traversed serially, one after the
other. You can also think of the list as a multidimensional array, but the list offers more features. A
list item can have subitems and can be sorted according to any column. For example, you can set
up a list of customer names (the list’s items) and assign a number of subitems to each customer:
a contact, an address, a phone number, and so on. Or you can set up a list of files with their
attributes as subitems. Figure 9.4 shows a Windows folder mapped on a ListView control. Each
file is an item, and its attributes are the subitems. As you already know, you can sort this list by
filename, size, file type, and so on. All you have to do is click the header of the corresponding
column.
The ListView control is a glorified ListBox control. If all you need is a control to store sorted
objects, use a ListBox control. If you want more features, such as storing multiple items per row,Petroutsos c09.tex V2 - 01/28/2008 1:28pm Page 309
UNDERSTANDING THE LISTVIEW, TREEVIEW, AND IMAGELIST CONTROLS 309
sorting them in different ways, or locating them based on any subitem’s value, you must consider
the ListView control. You can also look at the ListView control as a view-only grid.
Figure 9.4
A folder’s files displayed
in a ListView control
(Details view)
The TreeView and ListView controls are commonly used along with the ImageList control. The
ImageList control is a simple control for storing images so they can be retrieved quickly and used
at runtime. You populate the ImageList control with the images you want to use on your interface,
usually at design time, and then you recall them by an index value at runtime. Before we get into
the details of the TreeView and ListView controls, a quick overview of the ImageList control is
in order.
The ImageList Control
The ImageList is a simple control that stores images used by other controls at runtime. For
example, a TreeView control can use icons to identify its nodes. The simplest and quickest method
of preparing these images is to create an ImageList control and add to it all the icons you need for
decorating the TreeView control’s nodes. The ImageList control maintains a series of bitmaps in
memory that the TreeView control can access quickly at runtime. Keep in mind that the ImageList
control can’t be used on its own and remains invisible at runtime.
To use the ImageList control in a project, double-click its icon in the Toolbox (you’ll find it in the
Components tab) to place an instance of the control on your form. To load images to an ImageList
control, locate the Images property in the Properties window and click the ellipsis button next
to the property name. Alternatively, you can select the Choose Images command of the control’s
context menu. The Images Collection Editor dialog box (see Figure 9.5) will pop up, and you can
load all the images you want by selecting the appropriate files. All the images should have the
same dimensions — but this is not a requirement. Notice that the ImageList control doesn’t
resize the images; you must make sure that they have the proper sizes before loading them into
the control.
To add an image to the collection, click the Add button. You’ll be prompted to select an image
file through the Open File dialog box. Each image you select is added to the list. When you select
an image in this list, the properties of the image are displayed in the same dialog box — but you
can’t change these properties, except for the image’s name, which is the file’s name by default. Add
a few images and then close the Images Collection Editor. In the control’s Properties window, you
can set the size of all images and the TransparentColor property, which is a color that will bePetroutsos c09.tex V2 - 01/28/2008 1:28pm Page 310
310 CHAPTER 9 THE TREEVIEW AND LISTVIEW CONTROLS
treated as transparent for all images (this color is also known as the key color). The images will be
resized accordingly by the control as they’re displayed.
Figure 9.5
The Images Collection
Editor dialog box
The other method of adding images to an ImageList control is to call the Add method of the
Images collection, which contains all the images stored in the control. To add an image at runtime,
you must first create an Image object with the image (or icon) you want to add to the control and
then call the Add method as follows:
ImageList1.Images.Add(image)
where image is an Image object with the desired image. You will usually call this method as
follows:
ImageList1.Images.Add(Image.FromFile(path))
where path is the full path of the file with the image.
The Images collection of the ImageList control is a collection of Image objects, not the files in
which the pictures are stored. This means that the image files need not reside on the computer
on which the application will be executed, as long as they have been added to the collection at
design time.
The TreeViewControl
Let’s start our discussion with a few simple properties that you can set at design time. To experi-
ment with the properties discussed in this section, open the TreeViewDemo project. The project’s
main form is shown in Figure 9.6. After setting some properties (they are discussed next), run the
project and click the Populate button to populate the control. After that, you can click the other
buttons to see the effect of the various property settings on the control.Petroutsos c09.tex V2 - 01/28/2008 1:28pm Page 311
THE TREEVIEW CONTROL 311
Figure 9.6
The TreeViewDemo
project demonstrates
the basic properties
and methods of the
TreeView control.
Here are the basic properties that determine the appearance of the control:
ShowCheckBoxes If this property is True, a check box appears in front of each node. If the
control displays check boxes, you can select multiple nodes; otherwise, you’re limited to a
single selection.
FullRowSelect This True/False value determines whether a node will be selected even if the
user clicks outside the node’s caption.
HideSelection This property determines whether the selected node will remain highlighted
when the focus is moved to another control. By default, the selected node doesn’t remain
highlighted when the control loses the focus.
HotTracking This property is another True/False value that determines whether nodes are
highlighted as the pointer hovers over them.When it’s True, the TreeView control behaves like
a web document with the nodes acting as hyperlinks — they turn blue while the pointer hovers
over them. Use the NodeMouseHover event to detect when the pointer hovers over a node.
Indent This property specifies the indentation level in pixels. The same indentation applies
to all levels of the tree—each level is indented by the same number of pixels with respect to its
parent level.
PathSeparator A node’s full name is made up of the names of its parent nodes, separated by
a backslash. To use a different separator, set this property to the desired symbol.
ShowLines The ShowLines property is a True/False value that determines whether the
control’s nodes will be connected to its parent items with lines. These lines help users visualize
the hierarchy of nodes, and it’s customary to display them.
ShowPlusMinus The ShowPlusMinus property is a True/False value that determines
whether the plus/minus button is shown next to the nodes that have children. The plus button
is displayed when the node is collapsed, and it causes the node to expand when clicked. Like-
wise, the minus sign is displayed when the node is expanded, and it causes the node to collapsePetroutsos c09.tex V2 - 01/28/2008 1:28pm Page 312
312 CHAPTER 9 THE TREEVIEW AND LISTVIEW CONTROLS
when clicked. Users can also expand the current node by pressing the left-arrow button and
collapse it with the right-arrow button.
ShowRootLines This is another True/False property that determines whether there will
be lines between each node and root of the tree view. Experiment with the ShowLines and
ShowRootLines properties to find out how they affect the appearance of the control.
Sorted This property determines whether the items in the control will be automatically
sorted. The control sorts each level of nodes separately. In our Globe example, it will sort the
continents, then the countries within each continent, and then the cities within each country.
Adding Nodes at Design Time
Let’s look now at the process of populating the TreeView control. Adding an initial collection of
nodes to a TreeView control at design time is trivial. Locate the Nodes property in the Properties
window, and you’ll see that its value is Collection. To add items, click the ellipsis button, and the
TreeNode Editor dialog box will appear, as shown in Figure 9.7. To add a root item, just click
the Add Root button. The new item will be named Node0 by default. You can change its caption
by selecting the item in the list and setting its Text property accordingly. You can also change the
node’s Name property, as well as the node’s appearance by using the NodeFont, FontColor,and
ForeColor properties.
Figure 9.7
The TreeNode Editor
dialog box
To specify an image for the node, set the control’s ImageList property to the name of an
ImageList control that contains the appropriate images, and then set either the node’s ImageKey
property to the name of the image, or the node’s ImageIndex property to the index of the desired
image in the ImageList control. If you want to display a different image when the control is
selected, set the SelectedImageKey or the SelectedImageIndex property accordingly.
You can add root items by clicking the Add Root button, or you can add items under the
selected node by clicking the Add Child button. Follow these steps to enter the root node with the
string Globe, a child node for Europe, and two more nodes under Europe: Germany and Italy. I’m
assuming that you’re starting with a clean control. If your TreeView control contains any items,
clear them all by selecting one item at a time in the list and pressing the Delete key, or clicking the
delete button (the one with the X icon) on the dialog box.Petroutsos c09.tex V2 - 01/28/2008 1:28pm Page 313
THE TREEVIEW CONTROL 313
Click the Add Root button first. A new node is added automatically to the list of nodes, and it is
named Node0. Select it with the mouse, and its properties appear in the right pane of the TreeNode
Editor window. Here you can change the node’s Text property to GLOBE. You can specify the
appearance of each node by setting its font and fore/background colors.
Then click the Add Child button, which adds a new node under the GLOBAL root node. Select
it with the mouse as before, and change its Text property to Europe. Then select the newly added
node in the list and click the Add Child button again. Name the new node Germany.You’ve
successfully added a small hierarchy of nodes. To add another node under Europe, select the
Europe node in the list and click the Add Child button again. Name the new item Italy.
Continue adding a few cities under each country. You might add child nodes under the wrong
parent, which can happen if you forget to select the proper parent node before clicking the Add
Child button. To delete a node, select it with themouse and click the Delete button.Note that when
a node is deleted, all the nodes under it are deleted, too. Moreover, this action can’t be undone. So
be careful when deleting nodes.
Click the OK button to close the TreeNode Editor’s window and return to your form. The
nodes you added to the TreeView control are there, but they’re collapsed. Only the root nodes are
displayed with the plus sign in front of their names. Click the plus sign to expand the tree and see
its child nodes. The TreeView control behaves the same at design time as it does at runtime — as
far as navigating the tree goes, at least.
The nodes added to a TreeView control at design time will appear each time the form is loaded.
You can add new nodes through your code, and you will see how this is done in the following
section.
Adding Nodes at Runtime
Adding items to the control at runtime is a bit more involved. All the nodes belong to the control’s
Nodes collection, which is made up of TreeNode objects. To access the Nodes collection, use
the following expression, where TreeView1 is the control’s name and Nodes is a collection of
TreeNode objects:
TreeView1.Nodes
This expression returns a collection of TreeNode objects and exposes the proper members for
accessing and manipulating the individual nodes. The control’s Nodes property is the collection of
all root nodes.
To access the first node, use the expression TreeView.Nodes(0) (thisistheGlobenodeinour
example). The Text property returns the node’s value, which is a string. TreeView1.Nodes(0).
Text is the caption of the root node on the control. The caption of the second node on the same
level is TreeView1.Nodes(1).Text,andsoon.
The following statements print the strings shown highlighted below them (these strings are not
part of the statements; they’re the output that the statements produce):
Debug.WriteLine(TreeView1.Nodes(0).Text)
GLOBE
Debug.WriteLine(TreeView1.Nodes(0).Nodes(0).Text)
Europe
Debug.WriteLine(TreeView1.Nodes(0).Nodes(0).Nodes(1).Text)
ItalyPetroutsos c09.tex V2 - 01/28/2008 1:28pm Page 314
314 CHAPTER 9 THE TREEVIEW AND LISTVIEW CONTROLS
Let’s take a closer look at these expressions. TreeView1.Nodes(0) is the first root node, the
Globe node. Under this node, there is a collection of nodes, the TreeView1.Nodes(0).Nodes
collection. Each node in this collection is a continent name. The first node in this collection is
Europe, and you can access it with the expression TreeView1.Nodes(0).Nodes(0).Ifyouwant
to change the appearance of the node Europe, type a period after the preceding expression to
access its properties (the NodeFont property to set its font, the ForeColor property to set it color,
the ImageIndex property, and so on). Likewise, this node has its own Nodes collection, which
contains the countries under the specific continent.
Adding New Nodes
The Add method adds a new node to the Nodes collection. The Add methodacceptsasanargument
a string or a TreeNode object. The simplest form of the Add method is
newNode = Nodes.Add(nodeCaption)
where nodeCaption is a string that will be displayed on the control. Another form of the Add
method allows you to add a TreeNode object directly (nodeObj is a properly initialized TreeNode
variable):
newNode = Nodes.Add(nodeObj)
To use this form of the method, you must first declare and initialize a TreeNode object:
Dim nodeObj As New TreeNode
nodeObj.Text = ”Tree Node”
nodeObj.ForeColor = Color.BlueViolet
TreeView1.Nodes.Add(nodeObj)
The last overloaded form of the Add method allows you to specify the index in the current
Nodes collection, where the node will be added:
newNode = Nodes.Add(index, nodeObj)
The nodeObj TreeNode object must be initialized as usual.
To add a child node to the root node, use a statement such as the following:
TreeView1.Nodes(0).Nodes.Add(”Asia”)
To add a country under Asia, use a statement such as the following:
TreeView1.Nodes(0).Nodes(1).Nodes.Add(”Japan”)
The expressions can get quite lengthy. The proper way to add child items to a node is to create
a TreeNode variable that represents the parent node, under which the child nodes will be added.
Let’s say that the ContinentNode variable in the following example represents the node Europe:
Dim ContinentNode As TreeNode
ContinentNode = TreeView1.Nodes(0).Nodes(2)Petroutsos c09.tex V2 - 01/28/2008 1:28pm Page 315
THE TREEVIEW CONTROL 315
Then you can add child nodes to the ContinentNode node:
ContinentNode.Nodes.Add(”France”)
ContinentNode.Nodes.Add(”Germany”)
To add yet another level of nodes, the city nodes, create a new variable that represents a specific
country. The Add method actually returns a TreeNode object that represents the newly added
node, so you can add a country and a few cities by using statements such as the following:
Dim CountryNode As TreeNode
CountryNode = ContinentNode.Nodes.Add(”Germany”)
CountryNode.Nodes.Add(”Berlin”)
CountryNode.Nodes.Add(”Frankfurt”)
Then you can continue adding countries under another continent as follows:
CountryNode = ContinentNode.Nodes.Add(”Italy”)
CountryNode.Nodes.Add(”Rome”)
The Nodes Collection Members
The Nodes collection exposes the usual members of a collection. The Count property returns the
number of nodes in the Nodes collection. Again, this is not the total number of nodes in the control,
just the number of nodes in the current Nodes collection. The expression
TreeView1.Nodes.Count
returns the number of all nodes in the first level of the control. In the case of the Globe example,
it returns the value 1. The expression
TreeView1.Nodes(0).Nodes.Count
returns the number of continents in the Globe example. Again, you can simplify this expression
by using an intermediate TreeNode object:
Dim Continents As TreeNode
Continents = TreeView1.Nodes(0)
Debug.WriteLine(
”There are ” & Continents.Nodes.Count.ToString &
” continents on the control”)
The Clear method removes all the child nodes from the current node. If you apply this method to
the control’s root node, it will clear the control. To remove all the cities under the Germany node,
use a statement such as the following:
TreeView1.Nodes(0).Nodes(2).Nodes(1).Nodes.Clear
This example assumes that the third node under Globe corresponds to Europe, and the second
node under Europe corresponds to Germany.Petroutsos c09.tex V2 - 01/28/2008 1:28pm Page 316
316 CHAPTER 9 THE TREEVIEW AND LISTVIEW CONTROLS
The Item property retrieves a node specified by an index value. The expression Nodes.Item(1)
is equivalent to the expression Nodes(1). Finally, the Remove method removes a node from the
Nodes collection. Its syntax is
Nodes.Remove(index)
where index istheorderofthenodeinthecurrent Nodes collection. To remove the selected node,
call the Remove method on the SelectedNode property without arguments:
TreeView1.SelectedNode.Remove
Or you can apply the Remove method to a TreeNode object that represents the node you want
to remove:
Dim Node As TreeNode
Node = TreeView1.Nodes(0).Nodes(7)
Node.Remove
There are four properties that allow you to retrieve any node at the current segment of the
tree: FirstNode, NextNode, PrevNode,and LastNode. Let’s say the current node is the Germany
node. The FirstNode property will return the first city under Germany (the first node in the
current segment of the tree), and LastNode will return the last city under Germany. PrevNode and
NextNode allow you to iterate through the nodes of the current segment: They return the next
and previous nodes on the current segment of the tree (the sibling nodes, as they’re called). See the
section called ‘‘Enumerating the Nodes Collection’’ later in this chapter for an example.
Basic Nodes Properties
There are a few properties you will find extremely handy as you program the TreeView control.
The IsVisible property is a True/False value indicating whether the node to which it’s applied
is visible. To bring an invisible node into view, call its EnsureVisible method:
If Not TreeView1.SelectedNode.IsVisible Then
TreeView1.EnsureVisible
End If
How can the selected node be invisible? It can, if you select it from within your code in a
search operation. The IsSelected property returns True if the specified node is selected, while
the IsExpanded property returns True if the specified node is expanded. You can toggle a node’s
state by calling its Toggle method. You can also expand or collapse a node by calling its Expand
or Collapse method, respectively. Finally, you can collapse or expand all nodes by calling the
CollapseAll or ExpandAll method of the TreeView control.
VB 2008 at Work: The TreeViewDemo Project
It’s time to demonstrate the members discussed so far with an example. The project you’ll build in
this section is the TreeViewDemo project. The project’s main form is shown in Figure 9.6.
The Add Categories button adds the three top-level nodes to the TreeView control via the
statements shown in Listing 9.1. These are the control’s root nodes. The other two Add buttons
add nodes under the root nodes.Petroutsos c09.tex V2 - 01/28/2008 1:28pm Page 317
THE TREEVIEW CONTROL 317
Listing 9.1: The Add Categories Button
Protected Sub AddCategories Click(...)
Handles AddCategories.Click
TreeView1.Nodes.Add(”Shapes”)
TreeView1.Nodes.Add(”Solids”)
TreeView1.Nodes.Add(”Colors”)
End Sub
When these statements are executed, three root nodes are added to the list. After clicking the
Add Categories button, your TreeView control looks like the one shown here.
To add a few nodes under the node Colors, you must retrieve the Colors Nodes collection and
add child nodes to this collection, as shown in Listing 9.2.
Listing 9.2: The Add Colors Button
Protected Sub AddColors Click(...)
Handles AddColors.Click
Dim cnode As TreeNode
cnode = TreeView1.Nodes(2)
cnode.Nodes.Add(”Pink”)
cnode.Nodes.Add(”Maroon”)
cnode.Nodes.Add(”Teal”)
End Sub
When these statements are executed, three more nodes are added under the Colors node, but
the Colors node won’t be expanded. Therefore, its child nodes won’t be visible. To see its child
nodes, you must double-click the Colors node to expand it (or click the plus sign in front of it,
if there is one). The same TreeView control with its Colors node expanded is shown to the left.
Alternatively, you can add a statement that calls the Expand method of the cnode object, after
adding the color nodes to the control:
cnode.Expand()Petroutsos c09.tex V2 - 01/28/2008 1:28pm Page 318
318 CHAPTER 9 THE TREEVIEW AND LISTVIEW CONTROLS
Run the project, click the first button (Add Categories), and then click the second button (Add
Colors). If you click the Add Colors button first, you’ll get a NullReferenceException, indicating
that the node can’t be inserted unless its parent node already exists. I added a few statements in
the TreeViewDemo project’s code to disable the buttons that generate similar runtime errors.
To add child nodes under the Shapes node, use the statements shown in Listing 9.3. This is the
Add Shapes button’s Click event handler.
Listing 9.3: The Add Shapes Button
Protected Sub AddShapes Click(...)
Handles AddShapes.Click
Dim snode As TreeNode
snode = treeview1.Nodes(0)
snode.Nodes.Add(”Square”)
snode.Nodes.Add(”Triangle”)
snode.Nodes.Add(”Circle”)
End Sub
If you run the project and click the three buttons in the order in which they appear on the
form, the TreeView control will be populated with colors and shapes. If you double-click the items
Colors and Shapes, the TreeView control’s nodes will be expanded.
Notice that the code knows the order of the root node to which it’s adding child nodes. This
approach doesn’t work with a sorted tree. If your TreeView control is sorted, you must create a
hierarchy of nodes explicitly by using the following statements:
snode = TreeView1.Nodes.Add(”Shapes”)
snode.Add(”Square”)
snode.Add(”Circle”)
snode.Add(”Triangle”)
These statements will work regardless of the control’s Sorted property setting. The three
shapes will be added under the Shapes node, and their order will be determined automatically.
Of course, you can always populate the control in any way you like and then turn on the Sorted
property.Petroutsos c09.tex V2 - 01/28/2008 1:28pm Page 319
THE TREEVIEW CONTROL 319
Inserting a Root Node
Let’s revise the code we’ve written so far to display all the nodes under a new header. In other
words, we’ll add a new node called Items that will act as the root node for existing nodes. It’s not
a common operation, but it’s an interesting example of how tomanipulate the nodes of a TreeView
control at runtime.
First, wemust add the new root node. Before we do so, however,wemust copy all the first-level
nodes into local variables. We’ll use these variables to add the current root nodes under the new
(and single) root node. There are three root nodes currently in our control, so we need three local
variables. The three variables are of the TreeNode type, and they’re set to the root nodes of the
original tree. Then we must clear the entire tree, add the new root node (the Items node), and
finally add all the copied nodes under the new root. The code behind the Move Tree button is
shown in Listing 9.4.
Listing 9.4: Moving an Entire Tree
Protected Sub MoveTree Click(...)
Handles bttnMoveTree.Click
Dim colorNode, shapeNode, solidNode As TreeNode
colorNode = TreeView1.Nodes(0)
shapeNode = TreeView1.Nodes(1)
solidNode = TreeView1.Nodes(2)
TreeView1.Nodes.Clear()
TreeView1.Nodes.Add(”Items”)
TreeView1.Nodes(0).Nodes.Add(colorNode)
TreeView1.Nodes(0).Nodes.Add(shapeNode)
TreeView1.Nodes(0).Nodes.Add(solidNode)
End Sub
You can revise this code so that it uses an array of Node objects instead of individual variables
to store all the root nodes. For a routine that will work with any tree, you must assume that the
number of nodes is unknown, so the ArrayList would be a better choice. The following loop stores
all the root nodes of the TreeView1 control to the TVList ArrayList:
Dim TVList As New ArrayList
Dim node As TreeNode
For Each node in TreeView1.Nodes
TVList.Add(node)
Next
Likewise, the following loop extracts the root nodes from the TVList ArrayList:
Dim node As TreeNode
Dim itm As Object
TreeView1.Nodes.Clear
For Each itm In TVList
node = CType(itm, TreeNode)Petroutsos c09.tex V2 - 01/28/2008 1:28pm Page 320
320 CHAPTER 9 THE TREEVIEW AND LISTVIEW CONTROLS
TreeView1.Nodes.Add(node)
Next
Enumerating the Nodes Collection
As you saw in the previous example, a Node object can include an entire tree under it. When we
move a node, it takes with it the entire Nodes collection located under it. You can scan all the
nodes in a Nodes collection by using a loop, which starts with the first node and then moves to the
next node with the help of the FirstNode and NextNode properties. The following loop prints
the names of all continents in the GlobeTree control:
Dim CurrentNode As TreeNode
CurrentNode = GlobeTree.Nodes(0).Nodes(0).FirstNode
While CurrentNode IsNot Nothing
Debug.WriteLine(CurrentNode.text)
CurrentNode = CurrentNode.NextNode
End While
The last property demonstrated by the TreeViewDemo project is the Sorted property, which
sorts the child nodes of the node to which it’s applied. When you set the Sorted property of a
node to True, every child node you attach to it will be inserted automatically in alphabetical order.
If you reset the Sorted property to False, any child nodes you attach will be appended to the end
of the existing sorted nodes.
VB 2008 at Work: The Globe Project
The Globe project demonstrates many of the techniques we’ve discussed so far. It’s not the
simplest example of a TreeView control, and its code is lengthy, but it will help you understand
how to manipulate nodes at runtime. Because TreeView is not a simple control, before ending this
section I want to show you a nontrivial example that you can use as a starting point for your own
custom applications.
The Globe project consists of a single form, which is shown in Figure 9.8. The TreeView control
at the left contains a rather obvious tree structure that shows continents, countries, and cities. The
control is initially populated with the continents, which were added at design time. The countries
and cities are added from within the form’s Load event handler. Although the continents were
added at design time, there’s no particular reason not to add them to the control at runtime. It
would have been simpler to add all the nodes at runtime by using the TreeNode Editor, but I
decided to add a few nodes at design time just for demonstration purposes.
When a node is selected from the TreeView control, its text is displayed in the TextBox controls
at the bottom of the form. When a continent name is selected, the continent’s name appears in
the first TextBox, and the other two TextBoxes are empty. When a country is selected, its name
appears in the second TextBox, and its continent appears in the first TextBox. Finally, when a city
is selected, it appears in the third TextBox, along with its country and continent in the other two
TextBoxes.
You can also use the same TextBox controls to add new nodes. To add a new continent, just
supply the name of the continent in the first TextBox and leave the other two empty. To add a new
country, supply its name in the second TextBox and the name of the continent it belongs to in the
first one. Finally, to add a city, supply a continent, country, and city name in the three TextBoxes.
The program will add new nodes as needed.Petroutsos c09.tex V2 - 01/28/2008 1:28pm Page 321
THE TREEVIEW CONTROL 321
Figure 9.8
The Globe project
Run the Globe application and expand the continents and countries to see the tree structure
of the data stored in the control. Add new nodes to the control, and enumerate these nodes by
clicking the buttons on the right-hand side of the form. These buttons list the nodes at a given
level (continents, countries, and cities). When you add new nodes, the code places them in their
proper place in the list. If you specify a new city and a new country under an existing continent,
a new country node will be created under the specified continent, and a new city node will be
inserted under the specified country.
Adding New Nodes
Let’s take a look at the code of the Globe project. We’ll start by looking at the code that populates
the TreeView control. The root node (GLOBE) and the continent names were added at design time
through the TreeNode Editor.
When the application starts, the code adds the countries to each continent and adds the cities
to each country. The code in the form’s Load event goes through all the continents already in
the control and examines their Text properties. Depending on the continent represented by the
current node, the code adds the corresponding countries and some city nodes under each
country node.
If the current node is Africa, the first country to be added is Egypt. The Egypt node is added
to the ContinentNode variable. The new node is returned as a TreeNode object and is stored in
the CountryNode variable. Then the code uses this object to add nodes that correspond to cities
under the Egypt node. The form’s Load event handler is quite lengthy, so I’m showing only the
code that adds the first country under each continent and the first city under each country (see
Listing 9.5). The variable GlobeNode is the root node of the TreeView control, and it was declared
and initialized with the following statement:
Dim GlobeNode As TreeNode = GlobeTree.Nodes(0)Petroutsos c09.tex V2 - 01/28/2008 1:28pm Page 322
322 CHAPTER 9 THE TREEVIEW AND LISTVIEW CONTROLS
Listing 9.5: Adding the Nodes of Africa
For Each ContinentNode In GlobeNode.Nodes
Select Case ContinentNode.Text
Case ”Europe”
CountryNode = ContinentNode.Nodes.Add(”Germany”)
CountryNode.Nodes.Add(”Berlin”)
Case ”Asia”
CountryNode = ContinentNode.Nodes.Add(”China”)
CountryNode.Nodes.Add(”Beijing”)
Case ”Africa”
CountryNode = ContinentNode.Nodes.Add(”Egypt”)
CountryNode.Nodes.Add(”Cairo”)
CountryNode.Nodes.Add(”Alexandria”)
Case ”Oceania”
CountryNode = ContinentNode.Nodes.Add(”Australia”)
CountryNode.Nodes.Add(”Sydney”)
Case ”N. America”
CountryNode = ContinentNode.Nodes.Add(”USA”)
CountryNode.Nodes.Add(”New York”)
Case ”S. America”
CountryNode = ContinentNode.Nodes.Add(”Argentina”)
End Select
Next
The remaining countries and their cities are added via similar statements, which you can
examine if you open the Globe project. Notice that the GlobeTree control could have been pop-
ulated entirely at design time, but this wouldn’t be much of a demonstration. Let’s move on to a
few more interesting aspects of programming the TreeView control.
Retrieving the Selected Node
The selected node is given by the property SelectedNode. After retrieving the selected node,
you can also retrieve its parent node and the entire path to the root node. The parent node of the
selected node is TreeView1.SelectedNode.Parent. If this node has a parent, you can retrieve
it by calling the Parent property of the previous expression. The FullPath property of a node
retrieves the selected node’s full path. The FullPath property of the Rome node is as follows:
GLOBE\Europe\Italy\Rome
The slashes separate the segments of the node’s path. As mentioned earlier, you can specify
any other character for this purpose by setting the control’s PathSeparator property.
To remove the selected node from the tree, call the Remove method:
TreeView1.SelectedNode.Remove
If the selected node is a parent control for other nodes, the Remove method will take with
it all the nodes under the selected one. To select a node from within your code, set thePetroutsos c09.tex V2 - 01/28/2008 1:28pm Page 323
THE TREEVIEW CONTROL 323
control’s SelectedNode property to the TreeNode object that represents the node you want
to select.
One of the operations you’ll want to perform with the TreeView control is to capture the
selection of a node. The TreeView control fires the BeforeSelect and AfterSelect events, which
notify your application about the selection of another node. If you need to know which node was
previously selected, you must use the BeforeSelect event. The second argument of both events
has two properties, TreeNode and Action, which let you find out the node that fired the event
and the action that caused it. The e.Node property is a TreeViewNode object that represents the
selected node. Use it in your code as you would use any other node of the control. The e.Action
property is a member of the TreeViewAction enumeration (ByKeyboard, ByMouse, Collapse,
Expand, Unknown). Use this property to find out the action that caused the event. The actions of
expanding and collapsing a tree branch fire their own events, which are the BeforeExpand/
AfterExpand and the BeforeCollapse/AfterCollapse events, respectively.
The Globe project retrieves the selected node and extracts the parts of the node’s path. The
individual components of the path are displayed in the three TextBox controls at the bottom of
the form. Listing 9.6 shows the event handler for the TreeView control’s AfterSelect event.
Listing 9.6: Processing the Selected Node
Private Sub GlobeTree AfterSelect(...)
Handles GlobeTree.AfterSelect
If GlobeTree.SelectedNode Is Nothing Then Exit Sub
Dim components() As String
txtContinent.Text = ””
txtCountry.Text = ””
txtCity.Text = ””
Dim separators() As Char
separators = GlobeTree.PathSeparator.ToCharArray
components =
GlobeTree.SelectedNode.FullPath.
ToString.Split(separators)
If components.Length > 1 Then
txtContinent.Text = components(1)
If components.Length > 2 Then
txtCountry.Text = components(2)
If components.Length > 3 Then
txtCity.Text = components(3)
End Sub
The Split method of the String data type extracts the parts of a string that are delimited by the
PathSeparator character (the backslash character). If any of the captions contain this character,
you should change the default to a different character by setting the PathSeparator property to
some other character.
The code behind the Delete Current Node and Expand Current Node buttons is simple. To
delete a node, call the selected node’s Remove method. To expand a node, call the selected node’s
Expand method.Petroutsos c09.tex V2 - 01/28/2008 1:28pm Page 324
324 CHAPTER 9 THE TREEVIEW AND LISTVIEW CONTROLS
Processing Multiple Selected Nodes
The GlobeTree control has its ShowCheckBoxes property set to True so that users can select
multiple nodes. I added this feature to demonstrate how you can allow users to select any number
of nodes and then process them.
As you will notice by experimenting with the TreeView control, you can select a node that has
subordinate nodes, but these nodes will not be affected; they will remain deselected (or selected,
if you have already selected them). In most cases, however, when we select a parent node, we
actually intend to select all the nodes under it. When you select a country, for example, you’re in
effect selecting not only the country, but also all the cities under it. The code of the Process Selected
Nodes button assumes that when a parent node is selected, the code must also select all the nodes
under it.
Let’s look at the code that iterates through the control’s nodes and isolates the selected ones.
It doesn’t really process them; it simply prints their captions in the ListBox control. However,
you can call a function to process the selected nodes in any way you like. The code behind the
Process Selected Nodes button starts with the continents. It creates a TreeNodeCollection with
all the continents and then goes through the collection with a For Each...Next loop. At each step,
it creates another TreeNodeCollection, which contains all the subordinate nodes (the countries
under the selected continent) and goes through the new collection. This loop is also interrupted
at each step to retrieve the cities in the current country and process them with another loop. The
code behind the Process Selected Nodes button is straightforward, as you can see in Listing 9.7.
Listing 9.7: Processing All Selected Nodes
Protected Sub bttnProcessSelected Click(...)
Handles bttnProcessSelected.Click
Dim continent, country, city As TreeNode
Dim Continents, Countries, Cities As TreeNodeCollection
ListBox1.Items.Clear()
Continents = GlobeTree.Nodes(0).Nodes
For Each continent In Continents
If continent.Checked Then ListBox1.Items.Add(continent.FullPath)
Countries = continent.Nodes
For Each country In Countries
If country.Checked Or country.Parent.Checked Then
ListBox1.Items.Add(” ” & country.FullPath)
Cities = country.Nodes
For Each city In Cities
If city.Checked Or city.Parent.Checked Or
city.Parent.Parent.Checked Then
ListBox1.Items.Add(” ” & city.FullPath)
Next
Next
Next
End Sub
Thecodeexaminesthe Checked property of the current node, as well as the Checked property
of the parent node, all the way to the root node. If any of them is True, the node is consideredPetroutsos c09.tex V2 - 01/28/2008 1:28pm Page 325
THE TREEVIEW CONTROL 325
selected. You should try to add the appropriate code to select all subordinate nodes of a parent
node when the parent node is selected (whether you deselect the subordinate nodes when the
parent node is deselected is entirely up to you and depends on the type of application you’re
developing). The Nodes collection exposes the GetEnumerator method, and you can revise the last
listing so that it uses an enumerator in place of each For Each...Next loop. If you want to retrieve
the selected nodes only, and ignore the unselected child nodes of a selected parent node, use the
CheckedNodes collection.
Adding New Nodes
The Add This Node button lets the user add new nodes to the tree at runtime. The number and
type of the node(s) added depend on the contents of the TextBox controls:
◆ If only the first TextBox control contains text, a new continent will be added.
◆ If the first two TextBox controls contain text:
◆ If the continent exists, a new country node is added under the specified continent.
◆ If the continent doesn’t exist, a new continent node is added, and then a new country
node is added under the continent’s node.
◆ If all three TextBox controls contain text, the program adds a continent node (if needed),
then a country node under the continent node (if needed), and finally, a city node under
the country node.
Obviously, you can omit a city, or a city and country, but you can’t omit a continent name.
Likewise, you can’t specify a city without a country, or a country without a continent. The code
will prompt you accordingly when it detects any condition that prevents it from adding the new
node. If the node exists already, the program selects the existing node and doesn’t issue any
warnings. The Add This Node button’s code is shown in Listing 9.8.
Listing 9.8: Adding Nodes at Runtime
Private Sub bttnAddNode Click(...)
Handles bttnAddNode.Click
Dim nd As TreeNode
Dim Continents As TreeNode
If txtContinent.Text.Trim <> ”” Then
Continents = GlobeTree.Nodes(0)
Dim ContinentFound, CountryFound, CityFound As Boolean
Dim ContinentNode, CountryNode, CityNode As TreeNode
For Each nd In Continents.Nodes
If nd.Text.ToUpper = txtContinent.Text.ToUpper Then
ContinentFound = True
Exit For
End If
Next
If Not ContinentFound Then
nd = Continents.Nodes.Add(txtContinent.Text)
End IfPetroutsos c09.tex V2 - 01/28/2008 1:28pm Page 326
326 CHAPTER 9 THE TREEVIEW AND LISTVIEW CONTROLS
ContinentNode = nd
If txtCountry.Text.Trim <> ”” Then
Dim Countries As TreeNode
Countries = ContinentNode
If Not Countries Is Nothing Then
For Each nd In Countries.Nodes
If nd.Text.ToUpper = txtCountry.Text.ToUpper Then
CountryFound = True
Exit For
End If
Next
End If
If Not CountryFound Then
nd = ContinentNode.Nodes.Add(txtCountry.Text)
End If
CountryNode = nd
If txtCity.Text.Trim <> ”” Then
Dim Cities As TreeNode
Cities = CountryNode
If Not Cities Is Nothing Then
For Each nd In Cities.Nodes
If nd.Text.ToUpper = txtCity.Text.ToUpper Then
CityFound = True
Exit For
End If
Next
End If
If Not CityFound Then
nd = CountryNode.Nodes.Add(txtCity.Text)
End If
CityNode = nd
End If
End If
End If
End Sub
The listing is quite lengthy, but it’s not hard to follow. First, it attempts to find a continent
that matches the name in the first TextBox. If it succeeds, it does not need to add a new continent
node. If not, a new continent node must be added. To avoid simple data-entry errors, the code
converts the continent names to uppercase before comparing them to the uppercase of each node’s
name. The same happens with the countries and the cities. As a result, each node’s pathname
is unique — you can’t have the same city name under the same country more than once. It is
possible, however, to add the same city name to two different countries.
Listing Continents/Countries/Cities
The three buttons ListContinents, ListCountries, and ListCities populate the ListBox control with
the names of the continents, countries, and cities, respectively. The code is straightforward and
is based on the techniques discussed in previous sections. To print the names of the continents,Petroutsos c09.tex V2 - 01/28/2008 1:28pm Page 327
THE TREEVIEW CONTROL 327
it iterates through the children of the GLOBE node. Listing 9.9 shows the complete code of the
ListContinents button.
Listing 9.9: Retrieving the Continent Names
Private Sub bttnListContinents Click(...)
Handles bttnListContinents.Click
Dim Nd As TreeNode, continentNode As TreeNode
Dim continent As Integer, continents As Integer
ListBox1.Items.Clear()
Nd = GlobeTree.Nodes(0)
continents = Nd.Nodes.Count
continentNode = Nd.Nodes(0)
For continent = 1 To continents
ListBox1.Items.Add(continentNode.Text)
continentNode = continentNode.NextNode
Next
End Sub
The code behind the ListCountries button is equally straightforward, although longer. It must
scan each continent, and within each continent, it must scan in a similar fashion the continent’s
child nodes. To do this, you must set up two nested loops: the outer one to scan the continents,
and the inner one to scan the countries. The complete code for the ListCountries button is shown
in Listing 9.10. Notice that in this example, I used For...Next loops to iterate through the current
level’s nodes, and I also used the NextNode method to retrieve the next node in the sequence.
Listing 9.10: Retrieving the Country Names
Private Sub bttnListCountries Click(...)
Handles bttnListCountries.Click
Dim Nd, CountryNode, ContinentNode As TreeNode
Dim continent, continents, country, countries As Integer
ListBox1.Items.Clear()
Nd = GlobeTree.Nodes.Item(0)
continents = Nd.Nodes.Count
ContinentNode = Nd.Nodes(0)
For continent = 1 To continents
countries = ContinentNode.Nodes.Count
CountryNode = ContinentNode.Nodes(0)
For country=1To countries
ListBox1.Items.Add(CountryNode.Text)
CountryNode = CountryNode.NextNode
Next
ContinentNode = ContinentNode.NextNode
Next
End SubPetroutsos c09.tex V2 - 01/28/2008 1:28pm Page 328
328 CHAPTER 9 THE TREEVIEW AND LISTVIEW CONTROLS
When the ContinentNode.Next method is called, it returns the next node in the Continents
level. Then the property ContinentNode.Nodes(0) returns the first node in the Countries level.
As you can guess, the code of the ListCities button uses the same two nested lists as the previous
listing and an added inner loop, which scans the cities of each country.
The code behind these command buttons requires some knowledge of the information stored
in the tree. The code will work with trees that have two or three levels of nodes such as the Globe
tree, but what if the tree’s depth is allowed to grow to a dozen levels? A tree that represents the
structure of a folder on your hard disk, for example, might easily contain a dozen nested folders.
Obviously, to scan the nodes of this tree, you can’t put together unlimited nested loops. The next
section describes a technique for scanning any tree, regardless of how many levels it contains.
Finally, the application’s File menu contains commands for storing the nodes to a file and load-
ing the same nodes in a later session. These commands use serialization, a topic that’s discussed
in detail in Chapter 16, ‘‘XML and Object Serialization.’’ For now, you can use these commands to
persist the edited nodes to a disk file and read them back.
Scanning the TreeView Control
You have seen how to scan the entire tree of the TreeView control by using a For Each...Next
loop that iterates through the Nodes collection. This technique, however, requires that you know
the structure of the tree, and you must write as many nested loops as there are nested levels of
nodes. It works with simple trees, but it’s quite inefficient when it comes to mapping a file system
to a TreeView control. The following section explains how to iterate through a TreeView control’s
node, regardless of the nesting depth.
VB 2008 at Work: The TreeViewScan Project
The TreeViewScan project, whose main form is shown in Figure 9.9, demonstrates the process of
scanning the nodes of a TreeView control. The form contains a TreeView control on the left, which
is populated with the same data as the Globe project, and a ListBox control on the right, in
which the tree’s nodes are listed. Child nodes in the ListBox control are indented according to the
level to which they belong.
Scanning the child nodes in a tree calls for a recursive procedure: a procedure that calls itself.
Think of a tree structure that contains all the files and folders on your C: drive. If this structure
contained no subfolders, you’d need to set up a loop to scan each folder, one after the other.
Because most folders contain subfolders, the process must be interrupted at each folder to scan the
subfolders of the current folder. The process of scanning a drive recursively is described in detail
in Chapter 15, ‘‘Accessing Folders and Files.’’
Recursive Scanning of the Nodes Collection
To scan the nodes of the TreeView1 control, start at the top node of the control by using the
following statement:
ScanNode(GlobeTree.Nodes(0))
This is the code behind the Scan Tree button, and it doesn’t get any simpler. It calls the
ScanNode() subroutine to scan the child nodes of a specific node, which is passed to the sub-
routine as an argument. GlobeTree.Nodes(0) is the root node. By passing the root node to the
ScanNode() subroutine, we’re in effect asking it to scan the entire tree.Petroutsos c09.tex V2 - 01/28/2008 1:28pm Page 329
THE TREEVIEW CONTROL 329
Figure 9.9
The TreeViewScan appli-
cation demonstrates
how to scan the nodes
of a TreeView control
recursively.
This example assumes that the TreeView control contains a single root node and that all other
nodes are under the root node. If your control contains multiple root nodes, then you must set up
a small loop and call the ScanNode() subroutine once for each root node:
For Each node In GlobeTree.Nodes
ScanNode(node)
Next
Let’s look now at the ScanNode() subroutine shown in Listing 9.11.
Listing 9.11: Scanning a Tree Recursively
Sub ScanNode(ByVal node As TreeNode)
Dim thisNode As TreeNode
Static indentationLevel As Integer
Application.DoEvents()
ListBox1.Items.Add(Space(indentationLevel) & node.Text)
If node.Nodes.Count > 0 Then
indentationLevel += 5
For Each thisNode In node.Nodes
ScanNode(thisNode)
Next
indentationLevel -= 5
End If
End SubPetroutsos c09.tex V2 - 01/28/2008 1:28pm Page 330
330 CHAPTER 9 THE TREEVIEW AND LISTVIEW CONTROLS
This subroutine is deceptively simple. First, it adds the caption of the current node to the
ListBox1 control. If this node (represented by the Node variable) contains child nodes, the code
must scan them all. The Node.Nodes.Count method returns the number of nodes under the cur-
rent node; if this value is positive, we must scan all the items of the Node.Nodes collection. To do
this, the ScanNode() subroutine must call itself, passing a different argument each time. If you’re
familiar with recursive procedures, you’ll find the code quite simple. You may find the notion of
a function calling itself a bit odd, but it’s no different from calling another function. The execution
of the function that makes the call is suspended until the called function returns.
You can use the ScanNode() subroutine as is to scan any TreeView control. All you need is a
reference to the root node (or the node you want to scan recursively), which you must pass to the
ScanNode() subroutine as an argument. The subroutine will scan the entire subtree and display its
nodes in a ListBox control. The nodes will be printed one after the other. To make the list easier to
read, the code indents the names of the nodes by an amount that’s proportional to the nesting level.
Nodes of the first level aren’t indented at all. Nodes on the second level are indented by 5 spaces,
nodes on the third level are indented by 10 spaces, and so on. The variable indentationLevel
keeps track of the nesting level and is used to specify the indentation of the corresponding node.
It’s increased by 5 when we start scanning a new subordinate node and decreased by the same
amount when we return to the next level up. The indentationLevel variable is declared as Static
so that it maintains its value between calls.
Run the TreeViewScan project and expand all nodes. Then click the Scan Tree button to
populate the list on the right with the names of the continents/countries/cities. Obviously, the
ListBox control is not a substitute for the TreeView control. The data have no particular structure;
even when the names are indented, there are no tree lines connecting the nodes, and users can’t
expand and collapse the control’s contents.
The ListViewControl
The ListView control is similar to the ListBox control except that it can display its items in many
forms, along with any number of subitems for each item. To use the ListView control in your
project, place an instance of the control on a form and then set its basic properties, which are
described in the following list.
View and Arrange Two properties determine how the various items will be displayed on
the control: the View property, which determines the general appearance of the items, and the
Arrange property, which determines the alignment of the items on the control’s surface. The
View property can have one of the values shown in Table 9.1.
The Arrange property can have one of the settings shown in Table 9.2.
HeaderStyle This property determines the style of the headers in Details view. It has no
meaning when the View property is set to anything else, because only the Details view has
columns. The possible settings of the HeaderStyle property are shown in Table 9.3.
AllowColumnReorder This property is a True/False value that determines whether the user
can reorder the columns at runtime, and it’s meaningful only in Details view. If this property
is set to True, the user can move a column to a new location by dragging its header with the
mouse and dropping it in the place of another column.
Activation This property, which specifies how items are activated with the mouse, can have
one of the values shown in Table 9.4.Petroutsos c09.tex V2 - 01/28/2008 1:28pm Page 331
THE LISTVIEW CONTROL 331
Table 9.1: Settings of the View Property
Setting Description
LargeIcon (Default) Each item is represented by an icon and a caption below the icon.
SmallIcon Each item is represented by a small icon and a caption that appears to the right of the icon.
List Each item is represented by a caption.
Details Each item is displayed in a column with its subitems in adjacent columns.
Tile Each item is displayed with an icon and its subitems to the right of the icon. This view is
available only on Windows XP and Windows Server 2003.
Table 9.2: Settings of the Arrange Property
Setting Description
Default When an item is moved on the control, the item remains where it is dropped.
Left Items are aligned to the left side of the control.
SnapToGrid Items are aligned to an invisible grid on the control. When the user moves an item, the item
moves to the closest grid point on the control.
Top Items are aligned to the top of the control.
Table 9.3: Settings of the HeaderStyle Property
Setting Description
Clickable Visible column header that responds to clicking
Nonclickable (Default) Visible column header that does not respond to clicking
None No visible column header
Table 9.4: Settings of the Activation Property
Setting Description
OneClick Items are activated with a single click. When the cursor is over an item, it changes shape, and
the color of the item’s text changes.
Standard (Default) Items are activated with a double-click. No change in the selected item’s text color
takes place.
TwoClick Items are activated with a double-click, and their text changes color as well.Petroutsos c09.tex V2 - 01/28/2008 1:28pm Page 332
332 CHAPTER 9 THE TREEVIEW AND LISTVIEW CONTROLS
FullRowSelect This property is a True/False value, indicating whether the user can select an
entire row or just the item’s text, and it’s meaningful only in Details view. When this property
is False, only the first item in the selected row is highlighted.
GridLines Another True/False property. If True, grid lines between items and subitems are
drawn. This property is meaningful only in Details view.
Group The items of the ListView control can be grouped into categories. To use this feature,
you must first define the groups by using the control’s Group property, which is a collection of
strings. You can add as many members to this collection as you want. After that, as you add
items to the ListView control, you can specify the group to which they belong. The control will
group the items of the same category together and display the group’s title above each group.
You can easily move items between groups at runtime by setting the corresponding item’s
Group property to the name of the desired group.
LabelEdit The LabelEdit property lets you specify whether the user will be allowed to edit
the text of the items. The default value of this property is False. Notice that the LabelEdit
property applies to the item’s Text property only; you can’t edit the subitems (unfortunately,
you can’t use the ListView control as an editable grid).
MultiSelect A True/False value, indicating whether the user can select multiple items from
the control. To select multiple items, click them with the mouse while holding down the Shift
or Ctrl key. If the control’s ShowCheckboxes property is set to True, users can select multiple
items by marking the check box in front of the corresponding item(s).
Scrollable A True/False value that determines whether the scroll bars are visible. Even if the
scroll bars are invisible, users can still bring any item into view. All they have to do is select an
item and then press the arrow keys as many times as needed to scroll the desired item
into view.
Sorting This property determines how the items will be sorted, and its setting can be None,
Ascending, or Descending. To sort the items of the control, call the Sort method, which sorts
the items according to their caption. It’s also possible to sort the items according to any of their
subitems, as explained in the section ‘‘Sorting the ListView Control’’ later in this chapter.
The Columns Collection
To display items in Details view, you must first set up the appropriate columns. The first column
corresponds to the item’s caption, and the following columns correspond to its subitems. If you
don’t set up at least one column, no items will be displayed in Details view. Conversely, the
Columns collection is meaningful only when the ListView control is used in Details view.
The items of the Columns collection are of the ColumnHeader type. The simplest way to set up
the appropriate columns is to do so at design time by using a visual tool. Locate and select the
Columns property in the Properties window, and click the ellipsis button next to the property. The
ColumnHeader Collection Editor dialog box will appear, as shown in Figure 9.10, in which you
can add and edit the appropriate columns.
Adding columns to a ListView control and setting their properties through the dialog box
shown in Figure 9.10 is quite simple. Don’t forget to size the columns according to the data you
anticipate storing in them and to set their headers.
It is also possible tomanipulate the Columns collection fromwithin your code as follows. Create
a ColumnHeader object for each column in your code, set its properties, and then add it to the
control’s Columns collection:Petroutsos c09.tex V2 - 01/28/2008 1:28pm Page 333
THE LISTVIEW CONTROL 333
Dim ListViewCol As New ColumnHeader
ListViewCol.Text = ”New Column”
ListViewCol.TextAlign = HorizontalAlignment.Center
ListViewCol.Width = 125
ListView1.Columns.Add(ListViewCol)
Figure 9.10
The ColumnHeader
Collection Editor dialog
box
Adding and Removing Columns at Runtime
To add a new column to the control, use the Add method of the Columns collection. The syntax of
the Add method is as follows:
ListView1.Columns.Add(header, width, textAlign)
The header argument is the column’s header (the string that appears on top of the items). The
width argument is the column’s width in pixels, and the last argument determines how the text
will be aligned. The textAlign argument can be Center, Left,or Right.
The Add method returns a ColumnHeader object, which you can use later in your code to
manipulate the corresponding column. The ColumnHeader object exposes a Name property, which
can’t be set with the Add method:
Header1 = TreeView1.Add(
”Column 1”, 60, ColAlignment.Left)
Header1.Name = ”Column1”
After the execution of these statements, the first column can be accessed not only by index, but
also by name.
To remove a column, call the Remove method:
ListView1.Columns(3).RemovePetroutsos c09.tex V2 - 01/28/2008 1:28pm Page 334
334 CHAPTER 9 THE TREEVIEW AND LISTVIEW CONTROLS
The indices of the following columns are automatically decreased by one. The Clear method
removes all columns from the Columns collection. Like all collections, the Columns collection
exposes the Count property, which returns the number of columns in the control.
ListView Items and Subitems
As with the TreeView control, the ListView control can be populated either at design time or at
runtime. To add items at design time, click the ellipsis button next to the ListItems property in
the Properties window. When the ListViewItem Collection Editor dialog box pops up, you can
enter the items, including their subitems, as shown in Figure 9.11.
Figure 9.11
The ListViewItem
Collection Editor dialog
box
Click the Add button to add a new item. Each item has subitems, which you can specify as
members of the SubItems collection. To add an item with three subitems, you must populate the
item’s SubItems collection with the appropriate elements. Click the ellipsis button next
to the SubItems property in the ListViewItem Collection Editor; the ListViewSubItem Collection
Editor will appear. This dialog box is similar to the ListViewItem Collection Editor dialog box,
and you can add each item’s subitems. Assuming that you have added the item called Item 1 in
the ListViewItem Collection Editor, you can add these subitems: Item 1-a, Item 1-b,and
Item 1-c. The first subitem (the one with zero index) is actually the main item of the control.
Notice that you can set other properties such as the color and font for each item, the check
box in front of the item that indicates whether the item is selected, and the image of the item. Use
this window to experiment with the appearance of the control and the placement of the items,
especially in Details view because subitems are visible only in this view. Even then, you won’t see
anything unless you specify headers for the columns. Note that you can add more subitems than
there are columns in the control. Some of the subitems will remain invisible.
Unlike the TreeView control, the ListView control allows you to specify a different appearance
for each item and each subitem. To set the appearance of the items, use the Font, BackColor,and
ForeColor properties of the ListViewItem object.Petroutsos c09.tex V2 - 01/28/2008 1:28pm Page 335
THE LISTVIEW CONTROL 335
Almost all ListView controls are populated at runtime. Not only that, but you should be able
to add and remove items during the course of the application. The items of the ListView control
are of the ListViewItem type, and they expose members that allow you to control the appearance
of the items on the control. These members are as follows:
BackColor/ForeColor properties These properties set or return the background/foreground
colors of the current item or subitem.
Checked property This property controls the status of an item. If it’s True, the item has been
selected. You can also select an item from within your code by setting its Checked property
to True. The check boxes in front of each item won’t be visible unless you set the control’s
ShowCheckBoxes property to True.
Font property This property sets the font of the current item. Subitems can be displayed in a
different font if you specify one by using the Font property of the corresponding subitem
(see the section titled ‘‘The SubItems Collection,’’ later in this chapter). By default, subitems
inherit the style of the basic item. To use a different style for the subitems, set the item’s
UseItemStyleForSubItems property to False.
Text property This property indicates the caption of the current item or subitem.
SubItems collection This property holds the subitems of a ListViewItem. To retrieve a
specific subitem, use a statement such as the following:
sitem = ListView1.Items(idx1).SubItems(idx2)
where idx1 is the index of the item, and idx2 is the index of the desired subitem.*
To add a new subitem to the SubItems collection, use the Add method, passing the text of the
subitem as an argument:
LItem.SubItems.Add(”subitem’s caption”)
The argument of the Add method can also be a ListViewItem object. Create a ListViewItem,
populate it, and then add it to the Items collection as shown here:
Dim LI As New ListViewItem
LI.Text = ”A New Item”
Li.SubItems.Add(”Its first subitem”)
Li.SubItems.Add(”Its second subitem”)
‘ statements to add more subitems
ListView1.Items.Add(LI)
If you want to add a subitem at a specific location, use the Insert method. The Insert method
of the SubItems collection accepts two arguments: the index of the subitem before which the
new subitem will be inserted, and a string or ListViewItem to be inserted:
LItem.SubItems.Insert(idx, subitem)
Like the ListViewItem objects, each subitem can have its own font, which is set with the Font
property.Petroutsos c09.tex V2 - 01/28/2008 1:28pm Page 336
336 CHAPTER 9 THE TREEVIEW AND LISTVIEW CONTROLS
The items of the ListView control can be accessed through the Items property, which is a
collection. As such, it exposes the standard members of a collection, which are described in the
following section. Its item has a SubItems collection that contains all the subitems of the
corresponding item.
The Items Collection
All the items on the ListView control forma collection: the Items collection. This collection exposes
the typical members of a collection that let you manipulate the control’s items. These members are
discussed next.
Add method This method adds a new item to the Items collection. The syntax of the Add
method is as follows:
ListView1.Items.Add(caption)
You can also specify the index of the image to be used, along with the item and a collection of
subitems to be appended to the new item, by using the following form of the Add method:
ListView1.Items.Add(caption, imageIndex)
where imageIndex is the index of the desired image on the associated ImageList control.
Finally, you can create a ListViewItem object in your code and then add it to the ListView
control by using the following form of the Add method:
ListView1.Items.Add(listItemObj)
The following statements create a new item, set its individual subitems, and then add the
newly created ListViewItem object to the control:
LItem.Text = ”new item”
LItem.SubItems.Add(”sub item 1a”)
LItem.SubItems.Add(”sub item 1b”)
LItem.SubItems.Add(”sub item 1c”)
ListView1.Items.Add(LItem)
Count property Returns the number of items in the collection.
Item property Retrieves an item specified by an index value.
Clear method Removes all the items from the collection.
Remove method Removes an item from the collection.
The SubItems Collection
Each item in the ListView controlmay have one or more subitems. You can think of the item as the
key of a record, and the subitems as the other fields of the record. The subitems are displayed only
in Details mode, but they are available to your code in any view. For example, you can display all
items as icons, and when the user clicks an icon, show the values of the selected item’s subitems
on other controls.Petroutsos c09.tex V2 - 01/28/2008 1:28pm Page 337
THE LISTVIEW CONTROL 337
To access the subitems of a given item, use its SubItems collection. The following statements
add an item and three subitems to the ListView1 control:
Dim LItem As ListViewItem
LItem = ListView1.Items.Add(”Alfred’s Futterkiste”)
LItem.SubItems.Add(”Maria Anders”)
LItem.SubItems.Add(”030-0074321”)
LItem.SubItems.Add(”030-0076545”)
To access the SubItems collection, you need a reference to the item to which the subitems
belong. The Add method returns a reference to the newly added item, the LItem variable, which is
then used to access the item’s subitems, as shown in the preceding code segment.
Displaying the subitems on the control requires some overhead. Subitems are displayed only
in Details view mode. However, setting the View property to Details is not enough. You must first
create the columns of the Details view, as explained earlier. The ListView control displays only as
many subitems as there are columns in the control. The first column, with the header Company,
displays the items of the list. The following columns display the subitems. Moreover, you can’t
specify which subitemwill be displayed under each header. The first subitem (Maria Anders in the
preceding example) will be displayed under the second header, the second subitem (030-0074321
in the same example) will be displayed under the third header, and so on. At runtime, the user
can rearrange the columns by dragging them with the mouse. To disable the rearrangement of the
columns at runtime, set the control’s AllowColumnReorder property to False (its default value
is True).
Unless you set up each column’s width, they will all have the same width. The width of
individual columns is specified in pixels, and you can set it to a percentage of the total width of
the control, especially if the control is docked to the form. The following code sets up a ListView
control with four headers, all having the same width:
Dim LWidth As Integer
LWidth = ListView1.Width - 5
ListView1.ColumnHeaders.Add(”Company”, LWidth / 4)
ListView1.ColumnHeaders.Add(”Contact”, LWidth / 4)
ListView1.ColumnHeaders.Add(”Phone”, LWidth / 4)
ListView1.ColumnHeaders.Add(”FAX”, LWidth / 4)
ListView1.View = DetailsView
This subroutine sets up four headers of equal width. The first header corresponds to the item
(not a subitem). The number of headers you set up must be equal to the number of subitems you
want to display on the control, plus one. The constant 5 is subtracted to compensate for the width
of the column separators. If the control is anchored to the vertical edges of the form, you must
execute these statements from within the form’s Resize event handler, so that the columns are
resized automatically as the control is resized.
VB 2008 at Work: The ListViewDemo Project
Let’s put together the members of the ListView control to create a sample application that
populates the control and enumerates its items. The sample application of this section is the
ListViewDemo project. The application’s form, shown in Figure 9.12, contains a ListView control
whose items can be displayed in all possible views, depending on the status of the RadioButton
controls in the List Style section on the right side of the form.Petroutsos c09.tex V2 - 01/28/2008 1:28pm Page 338
338 CHAPTER 9 THE TREEVIEW AND LISTVIEW CONTROLS
Figure 9.12
The ListViewDemo
project demonstrates
the basic members
of the ListView control.
The control’s headers and their widths were set at design time through the ColumnHeader
Collection Editor, as explained earlier. To populate the ListView control, click the Populate List
button, whose code is shown next. The code creates a new ListViewItem object for each item to be
added. Then it calls the Add method of the SubItems collection to add the item’s subitems (contact,
phone, and fax numbers). After the ListViewItem has been set up, it’s added to the control via the
Add method of its Items collection.
Listing 9.12 shows the statements that insert the first two items in the list. The remaining items
are added by using similar statements, which need not be repeated here. The sample data I used
in the ListViewDemo application came from the Northwind sample database.
Listing 9.12: Populating a ListView Control
Dim LItem As New ListViewItem()
LItem.Text = ”Alfred’s Futterkiste”
LItem.SubItems.Add(”Anders Maria”)
LItem.SubItems.Add(”030-0074321”)
LItem.SubItems.Add(”030-0076545”)
LItem.ImageIndex = 0
ListView1.Items.Add(LItem)
LItem = New ListViewItem()
LItem.Text = ”Around the Horn”
LItem.SubItems.Add(”Hardy Thomas”)
LItem.SubItems.Add(”(171) 555-7788”)
LItem.SubItems.Add(”(171) 555-6750”)
LItem.ImageIndex = 0
ListView1.Items.Add(LItem)Petroutsos c09.tex V2 - 01/28/2008 1:28pm Page 339
THE LISTVIEW CONTROL 339
Enumerating the List
The Enumerate List button scans all the items in the list and displays them along with their
subitems in the Immediate window. To scan the list, you must set up a loop that enumerates
all the items in the Items collection. For each item in the list, set up a nested loop that scans all the
subitems of the current item. The complete code for the Enumerate List button is shown in
Listing 9.13.
Listing 9.13: Enumerating Items and SubItems
Private Sub bttnEnumerate Click(...)
Handles bttnEnumerate.Click
Dim i, j As Integer
Dim LItem As ListViewItem
For i = 0 To ListView1.Items.Count - 1
LItem = ListView1.Items(i)
Debug.WriteLine(LItem.Text)
Forj=0To LItem.SubItems.Count - 1
Debug.WriteLine(” ” & ListView1.Columns(j).Text &
” ” & Litem.SubItems(j).Text)
Next
Next
End Sub
Notice that each item may have a different number of subitems. The output of this code in the
Immediate window is shown next. The subitems appear under the corresponding item, and they
are indented by three spaces:
Alfred’s Futterkiste
Company Alfred’s Futterkiste
Contact Anders Maria
Telephone 030-0074321
FAX 030-0076545
Around the Horn
Company Around the Horn
Contact Hardy Thomas
Telephone (171) 555-7788
FAX (171) 555-6750
The code in Listing 9.13 uses a For...Next loop to iterate through the items of the control. You
can also set up a For Each...Next loop, as shown here:
Dim LI As ListViewItem
For Each LI In ListView1.Items
{ access the current item through the LI variable}
NextPetroutsos c09.tex V2 - 01/28/2008 1:28pm Page 340
340 CHAPTER 9 THE TREEVIEW AND LISTVIEW CONTROLS
Sorting the ListView Control
The ListView control provides the Sort method, which sorts the list’s items, and the Sorting
property, which determines how the items will be sorted. The Sort methodsortstheitemsin
the first column alphabetically. Each item may contain any number of subitems, and you should
be able to sort the list according to any column. The values stored in the subitems can represent
different data types (numeric values, strings, dates, and so on), but the control doesn’t provide
a default sorting mechanism for all data types. Instead, it uses a custom comparer object, which
you supply, to sort the items. (The topic of building custom comparers is discussed in detail in
Chapter 14, ‘‘Storing Data in Collections.’’) A custom comparer is a function that compares two
items and returns an integer value (–1, 0, or 1) that indicates the order of the two items. After this
function is in place, the control uses it to sort its items.
The ListView control’s ListViewItemSorter property accepts the name of a custom comparer,
and the items on the control are sorted according to the custom comparer as soon as you call the
Sort method. You can provide several custom comparers and sort the items in many different
ways. If you plan to display subitems along with your items in Details view, you should make the
list sortable by any column. It’s customary for a ListView control to sort its items according to
the values in a specific column each time the header of this column is clicked. And this is
exactly the type of functionality you’ll add to the ListViewDemo project in this section.
The ListViewDemo control displays contact information. The items are company names, and
the first subitem under each item is the name of a contact. We’ll create two custom comparers to
sort the list according to either company name or contact. The two methods are identical because
they compare strings, but it’s not any more complicated to compare dates, distances, and so on.
Let’s start with the two custom comparers. Each comparer must be implemented in its own
class, and you assign the name of the custom comparer to the ListViewItem property of the
control. Listing 9.14 shows the ListCompanyComparer and ListContactComparer classes.
Listing 9.14: The Two CustomComparers for the ListViewDemo Project
Class ListCompanySorter
Implements IComparer
Public Function CompareTo(ByVal o1 As Object,
ByVal o2 As Object) As Integer
Implements System.Collections.IComparer.Compare
Dim item1, item2 As ListViewItem
item1 = CType(o1, ListViewItem)
item2 = CType(o2, ListViewItem)
If item1.ToString.ToUpper > item2.ToString.ToUpper Then
Return 1
Else
If item1.ToString.ToUpper < item2.ToString.ToUpper Then
Return -1
Else
Return 0
End If
End If
End Function
End ClassPetroutsos c09.tex V2 - 01/28/2008 1:28pm Page 341
THE LISTVIEW CONTROL 341
Class ListContactSorter
Implements IComparer
Public Function CompareTo(ByVal o1 As Object,
ByVal o2 As Object) As Integer
Implements System.collections.IComparer.Compare
Dim item1, item2 As ListVewItem
item1 = CType(o1, ListViewItem)
item2 = CType(o2, ListViewItem)
If item1.SubItems(1).ToString.ToUpper >
item2.SubItems(1).ToString.ToUpper Then
Return 1
Else
If item1.SubItems(1).ToString.ToUpper <
item2.SubItems(1).ToString.ToUpper Then
Return -1
Else
Return 0
End If
End If
End Function
End Class
The code is straightforward. If you need additional information, see the discussion of the
IComparer interface in Chapter 14. The two functions are identical, except that the first one sorts
according to the item, and the second one sorts according to the first subitem.
To test the custom comparers, you simply assign their names to the ListViewItemSorter
property of the ListView control. To take advantage of our custom comparers, we must write
some code that intercepts the clicks on the control’s headers and calls the appropriate comparer.
The ListView control fires the ColumnClick event each time a column header is clicked. This event
handler reports the index of the column that was clicked through the e.Column property, and
we can use this argument in our code to sort the items accordingly. Listing 9.15 shows the event
handler for the ColumnClick event.
Listing 9.15: The ListView Control’s ColumnClick Event Handler
Public Sub ListView1 ColumnClick(...)
Handles ListView1.ColumnClick
Select Case e.column
Case 0
ListView1.ListViewItemSorter = New ListCompanySorter()
ListView1.Sorting = SortOrder.Ascending
Case 1
ListView1.LisViewtItemSorter = New ListContactSorter()
ListView1.Sorting = SortOrder.Ascending
End Select
End SubPetroutsos c09.tex V2 - 01/28/2008 1:28pm Page 342
342 CHAPTER 9 THE TREEVIEW AND LISTVIEW CONTROLS
Processing Selected Items
The user can selectmultiple items froma ListView control by default. Even though you can display
a check mark in front of each item, it’s not customary. Multiple items in a ListView control are
selected with the mouse while holding down the Ctrl or Shift key.
The selected items form the SelectedListItemCollection, which is a property of the control.
You can iterate through this collection with a For...Next loop or through the enumerator object
exposed by the collection. In the following example, I use a For Each...Next loop. Listing 9.16
is the code behind the Selected Items button of the ListViewDemo project. It goes through the
selected items and displays each one of them, along with its subitems, in the Output window.
Notice that you can select multiple items in any view, even when the subitems are not visible.
They’re still there, however, and they can be retrieved through the SubItems collection.
Listing 9.16: Iterating the Selected Items on a ListView Control
Private Sub bttnIterate Click(...)
Handles bttnIterate.Click
Dim LItem As ListViewItem
Dim LItems As ListView.SelectedListViewItemCollection
LItems = ListView1.SelectedItems
For Each LItem In LItems
Debug.Write(LItem.Text & vbTab)
Debug.Write(LItem.SubItems(0).ToString & vbTab)
Debug.Write(LItem.SubItems(1).ToString & vbTab)
Debug.WriteLine(LItem.SubItems(2).ToString & vbTab)
Next
End Sub
Fitting More Data into a ListView Control
A fairly common problem in designing practical user interfaces with the ListView control is how to
display more columns than can be viewed in a reasonably sized window. This is especially true for
accounting applications, whichmay have several debit/credit/balance columns. It’s typical to display
these values for the previous period, the current period, and then the totals, or to display the period
values along with the corresponding values of the previous year, year-to-date values, and so on.
The first approach is to use a smaller font, but this won’t take you far. A more-practical approach is to
use two (or even more) rows on the control for displaying a single row of data. For example, you can
display credit and debit data in two rows, as shown in the following figure. This arrangement saves
you the space of one column on the screen. You could even display the balance on a third row and use
different colors. The auxiliary rows, which are introduced to accommodate more data on the control,
could have a different background color too.
Adding auxiliary columns is straightforward; just add an empty string for the cells that don’t change
values, because all rows must have the same structure. The first two rows of the ListView control in
the preceding screen capture were added by using the following statements:Petroutsos c09.tex V2 - 01/28/2008 1:28pm Page 343
THE LISTVIEW CONTROL 343
Dim LI As ListViewItem
LI = ListView1.Items.Add(”Customer 1”)
LI.SubItems.Add(”Paul Levin”)
LI.SubItems.Add(”NE”)
LI.SubItems.Add(”12,100.90”)
LI = ListView1.Items.Add(””)
LI.SubItems.Add(””)
LI.SubItems.Add(””)
LI.SubItems.Add(”7,489.30”)
LI.SubItems.Add((12100.9 - 7489.3).ToString(”#,###.00”))
If your code reacts to the selection of an item with the mouse, or the double-click event, you must
take into consideration that users may click an auxiliary row. The following If structure in the con-
trol’s SelectedIndexChanged event handler prints the item’s text, no matter which of the two rows
of an item are selected on the control:
If ListView1.SelectedItems.Count = 0 Then Exit Sub
Dim idx As Integer
If ListView1.SelectedItems(0).Index Mod 2 <> 0 Then
idx = ListView1.SelectedItems(0).Index - 1
Else
idx = ListView1.SelectedItems(0).Index
End If
Debug.WriteLine(ListView1.Items(idx).Text)
VB 2008 at Work: The CustomExplorer Project
The last example in this chapter combines the TreeView and ListView controls. It’s a fairly
advanced example, but I included it here for the most ambitious readers. It can also be used as
the starting point for many custom applications, so give it a try. You can always come back to this
project after you’ve mastered other aspects of the Framework, such as the FileIO namespace.
The CustomExplorer project, shown in Figure 9.13, displays a structured list of folders in the left
pane, and a list of files in the selected folder in the right pane. The left pane is populated when the
application starts, and it might take a while. On my Pentium system, it takes nearly 30 seconds to
populate the TreeView control with the structure of the Windows folder (which includes FallBack
folders and three versions of the Framework;more than 50,000 files in 1,700 folders in all). You can
expand any folder in this pane and view its subfolders. To view the files in a folder, click the folder
name, and the right pane will be populated with the names of the selected folder’s files, along withPetroutsos c09.tex V2 - 01/28/2008 1:28pm Page 344
344 CHAPTER 9 THE TREEVIEW AND LISTVIEW CONTROLS
other data, such as the file size, date of creation, and date of last modification. You haven’t seen the
classes for accessing folders and files yet, but you shouldn’t have a problem following the code. If
you have to, you can review the members of the IO namespace in Chapter 15, in which I discuss
in detail the same project’s code.
Figure 9.13
The CustomExplorer
project demonstrates
how to combine a
TreeView and a ListView
control on the same
form.
This section’s project is not limited to displaying folders and files; you can populate the two
controls with data from several sources. For example, you can display customers in the left pane
(and organize them by city or state) and display their related data, such as invoices and payments,
in the right pane. Or you can populate the left pane with product names, and the right pane
with the respective sales. In general, you can use the project as an interface for many types of
applications. You can even use it as a custom Explorer to add features that are specific to
your applications.
The TreeView control on the left pane is populated from within the Form’s Load event handler
subroutine with the subfolders of the C:\Program Files folder:
Dim Nd As New TreeNode()
Nd = TreeView1.Nodes.Add(”C:\Program Files”)
ScanFolder(”c:\Program Files”, ND)
The first argument is the name of the folder to be scanned, and the second argument is the root
node, under which the entire tree of the specified folder will appear. To populate the control with
the files of another folder or drive, change the name of the path accordingly. The code is short, and
all the work is done by the ScanFolder() subroutine. The ScanFolder() subroutine, which
is a short recursive procedure that scans all the folders under a specific folder, is shown in
Listing 9.17.
Listing 9.17: The ScanFolder() Subroutine
Sub ScanFolder(ByVal folderSpec As String,
ByRef currentNode As TreeNode)Petroutsos c09.tex V2 - 01/28/2008 1:28pm Page 345
THE LISTVIEW CONTROL 345
Dim thisFolder As FileIO.Folder
Dim allFolders As FileIO.FolderCollection
allFolders = My.Computer.FileSystem.
GetFolder(folderSpec).FindSubFolders(”*.*”)
For Each thisFolder In allFolders
Dim Nd As TreeNode
Nd = New TreeNode(thisFolder.FolderName)
currentNode.Nodes.Add(Nd)
folderSpec = thisFolder.FolderPath
ScanFolder(folderSpec, Nd)
Me.Text = ”Scanning ” & folderSpec
Me.Refresh()
Next
End Sub
The variable FolderSpec represents the current folder (the one passed to the ScanFolder()
subroutine as an argument). The code creates the allFolders collection, which contains all the
subfolders of the current folder. Then it scans every folder in this collection and adds its name to
the TreeView control. After adding a folder’s name to the TreeView control, the procedure must
scan the subfolders of the current folder. It does so by calling itself and passing another folder’s
name as an argument.
Notice that the ScanFolder()subroutine doesn’t simply scan a folder. It also adds a node to the
TreeView control for each new folder it runs into. That’s why it accepts two arguments: the name
of the current folder and the node that represents this folder on the control. All folders are placed
under their parent folder, and the structure of the tree represents the structure of your hard disk
(or the section of the hard disk you’re mapping on the TreeView control). All this is done with a
small recursive subroutine: the ScanFolder() subroutine.
Viewing a Folder’s Files
To view the files of a folder, click the folder’s name in the TreeView control. As explained earlier,
the action of the selection of a new node is detected with the AfterSelect event. The code in this
event handler, shown in Listing 9.18, displays the selected folder’s files on the ListView control.
Listing 9.18: Displaying a Folder’s Files
Private Sub TreeView1 AfterSelect(...)
Handles TreeView1.AfterSelect
Dim Nd As TreeNode
Dim pathName As String
Nd = TreeView1.SelectedNode
pathName = Nd.FullPath
ShowFiles(pathName)
End Sub
The ShowFiles() subroutine actually displays the filenames, and some of their properties, in
the specified folder on the ListView control. Its code is shown in Listing 9.19.Petroutsos c09.tex V2 - 01/28/2008 1:28pm Page 346
346 CHAPTER 9 THE TREEVIEW AND LISTVIEW CONTROLS
Listing 9.19: The ShowFiles() Subroutine
Sub ShowFiles(ByVal selFolder As String)
ListView1.Items.Clear()
Dim files As FileIO.FileCollection
Dim file As FileIO.File
files = My.Computer.FileSystem.GetFolder(selFolder).FindFiles(”*.*”)
Dim TotalSize As Long
For Each file In files
Dim LItem As New ListViewItem
LItem.Text = file.FileName
LItem.SubItems.Add(file.Size.ToString(”#,###”))
LItem.SubItems.Add(
FormatDateTime(file.CreatedTime,
DateFormat.ShortDate))
Item.SubItems.Add(
FormatDateTime(file.AccessedTime,
DateFormat.ShortDate))
ListView1.Items.Add(LItem)
TotalSize += file.Size
Next
Me.Text = Me.Text & ” [” & TotalSize.ToString(”#,###”) & ” bytes]”
End Sub
The ShowFiles()subroutine creates a ListItem for each file. The item’s caption is the file’s
name, the first subitem is the file’s length, and the other two subitems are the file’s creation and
last access times. You can add more subitems, if needed, in your application. The ListView control
in this example uses the Details view to display the items. As mentioned earlier, the ListView
control will not display any items unless you specify the proper columns through the Columns
collection. The columns, along with their widths and captions, were set at design time through the
ColumnHeader Collection Editor.
Additional Topics
The discussion of the CustomExplorer sample project concludes the presentation of the TreeView
and ListView controls. However, there are a few more interesting topics you might like to read
about, which weren’t included in this chapter. Like all Windows controls, the ListView control
doesn’t provide a Print method, which I think is essential for any application that displays data
on this control. In Chapter 20, ‘‘Printing with Visual Basic 2008,’’ you will find the code for print-
ing the items of the ListView control. The printout we’ll generate will have columns, just like
the control, but it will display long cells (items or subitems with long captions) in multiple text
lines. Finally, in Chapter 16 you’ll learn how to save the nodes of a TreeView control to a disk file
between sessions by using a technique known as serialization. In that chapter, you’ll find the code
behind the Load Nodes and Save Nodes buttons of the Globe project and a thorough explanation
of their function.Petroutsos c09.tex V2 - 01/28/2008 1:28pm Page 347
THE BOTTOM LINE 347
The BottomLine
Create and present hierarchical lists by using the TreeView control. The TreeView control
is used to display a list of hierarchically structured items. Each item in the TreeView con-
trol is represented by a TreeNode object. To access the nodes of the TreeView control, use
the TreeView.Nodes collection. The nodes under a specific node (in other words, the child
nodes) form another collection of Node objects, which you can access by using the expression
TreeView.Nodes(i).Nodes. The basic property of the Node object is the Text property, which
stores the node’s caption. The Node object exposes properties for manipulating its appearance
(its foreground/background color, its font, and so on).
Master It How will you set up a TreeView control with a book’s contents at design time?
Create and present lists of structured items by using the ListView control. The ListView
control stores a collection of ListViewItem objects, the Items collection, and can display them
inseveralmodes,asspecifiedbythe View property. Each ListViewItem object has a Text prop-
erty and the SubItems collection. The subitems are not visible at runtime unless you set the
control’s View property to Details and set up the control’s Columns collection. There must be a
column for each subitem you want to display on the control.
Master It How will you set up a ListView control with three columns to display names,
emails, and phone numbers at design time?
Master It How would you populate the same control with the same data at runtime?Petroutsos c09.tex V2 - 01/28/2008 1:28pm Page 348Petroutsos c10.tex V3 - 01/28/2008 1:32pm Page 349
Chapter 10
Building Custom Classes
Classes are practically synonymous with objects and they’re at the very heart of programming
with Visual Basic. The controls you use to build the visible interface of your application are objects,
and the process of designing forms consists of setting the properties of these objects, mostly with
point-and-click operations. The Framework itself is an enormous compendium of classes, and you
can import any of them into your applications and use them as if their members were part of the
language. You simply declare a variable of the specific class type, initialize it, and then use it in
your code.
You have already worked with ListViewItem objects; they’re the items that make up the
contents of a ListView control. You declare an object of this type, then set its properties, and
finally add it to the control’s Items collection:
Dim LI As New ListViewItem
LI.Text = ”Item 1”
LI.Font = New Font(”Verdana”, 12, FontStyle.Regular)
LI is an object of the ListViewItem type. The New keyword creates a new instance of the
ListViewItem class; in other words, a new object. The following two statements set the basic prop-
erties of the LI variable. The Font property is also an object: it’s an instance of the Font class. You
can also add a few subitems to the LI variable and set their properties. When you’re finished,
you can add the LI variable to the ListView control:
ListView1.Items.Add(LI)
Controls are also objects; they differ from other classes in that controls provide a visual inter-
face, whereas variables don’t. However, you manipulate all objects by setting their properties and
calling their methods.
In this chapter, you’ll learn how to do the following:
◆ Build your own classes
◆ Use custom classes in your projects
◆ Customize the usual operators for your classes
Classes andObjects
When you create a variable of any type, you’re creating an instance of a class. The variable lets you
access the functionality of the class through its properties and methods. Even the base data types
are implemented as classes (the System.Integer class, System.Double, and so on). An integer value,Petroutsos c10.tex V3 - 01/28/2008 1:32pm Page 350
350 CHAPTER 10 BUILDING CUSTOM CLASSES
such as 3, is an instance of the System.Integer class, and you can call the properties and methods
of this class by using its instance. Expressions such as CDec(3).MinValue and #1/1/2000#.Today
are odd but valid. The first expression returns the minimum value you can represent with the
Decimal data type, whereas the second expression returns the current date. The DataTime data
type exposes the Today property, which returns the current date. The expression #1/1/2000# is a
value of the DataTime type, so you can find out the current date by calling its Today property. If
you enter either one of the preceding expressions in your code, you’ll get a warning, but they will
be executed.
Classes are used routinely in developing applications, and you should get into the habit of
creating and using custom classes, even with simple projects. In team development, classes are
a necessity, because they allow developers to share their work easily. If you’re working in a
corporate environment, in which different programmers code different parts of an application,
you can’t afford to repeat work that someone else has already done. You should be able to get
their code and use it in your application as is. That’s easier said than done, because you can guess
what will happen as soon as a small group of programmers start sharing code — they’ll end up
with dozens of different versions for each function, and every time a developer upgrades a func-
tion, he or she will most likely break the applications that were working with the old version. Or
each time they revise a function, they must update all the projects by using the old version of the
function and test them. It just doesn’t work.
The major driving force behind object-oriented programming (OOP) is code reuse. Classes allow
you to write code that can be reused in multiple projects. You already know that
classes don’t expose their source code. In other words, you can use a class without having access
to its code, and therefore you can’t affect any other projects that use the class. You also know
that classes implement complicated operations and make these operations available to program-
mers through properties and methods. The Array class exposes a Sort method, which sorts its
elements. This is not a simple operation, but fortunately you don’t have to know anything about
sorting. Someone else has done it for you and made this functionality available to your applica-
tions. This is called encapsulation. Some functionality has been built into the class (or encapsulated
into the class), and you can access it from within your applications by using a simple method call.
The Framework is made up of thousands of classes, which allow you to access all the func-
tionality of the operating system. You don’t have to see the code, and you don’t have to know
anything about sorting to sort your arrays, just as you don’t need to know anything about encryp-
tion to encrypt a string by using the System.Security.Cryptography class. In effect, you’re reusing
code that Microsoft has already written. It is also possible to extend these classes by adding
custom members, and even override existing members. When you extend a class, you create a
new class based on an existing one. Projects using the original class will keep seeing the original
class, and they will work fine. New projects that see the derived class will also work.
What Is a Class?
A class is a program that doesn’t run on its own; it’s a collection of methods that must be used by
another application.We exploit the functionality of the class by creating a variable of the same type
as the class and then call the class’s properties and methods through this variable. The methods
and properties of the class, as well as its events, constitute the class’s interface. It’s not a visible
interface, like the ones you’ve learned to design so far, because the class doesn’t interact directly
with the user. To interact with the class, the application uses the class’s interface, just as users will
be interacting with your application through its visual interface.Petroutsos c10.tex V3 - 01/28/2008 1:32pm Page 351
WHAT IS A CLASS? 351
You have already learned how to use classes. Now is the time to understand what goes on
behind the scenes when you interact with a class and its members. Behind every object, there’s a
class. When you declare an array, you’re invoking the System.Array class, which contains all the
code for manipulating arrays. Even when you declare a simple integer variable, you’re invoking a
class: the System.Integer class. This class contains the code that implements the various properties
(such as MinValue and MaxValue) and methods (such as ToString) of the Integer data type. The
first time you use an object in your code, you’re instantiating the class that implements this object.
The class’s code is loaded into memory, initializes its variables, and is ready to execute. The image
of the class in memory is said to be an instance of the class, and this is an object.
Classes versus Objects
Two of the most misused terms in OOP are object and class, and most people use them interchange-
ably. You should think of the class as the factory that produces objects. There’s only one System.Array
class, but you can declare any number of arrays in your code. Every array is an instance of the
System.Array class. All arrays in an application are implemented by the same code, but they store
different data. Each instance of a class is nothing more than a set of variables: the same code acts
on different sets of variables; each set of variables is a separate instance of the class.
Consider three TextBox controls on the same form. They are all instances of the System.Windows.
Forms.TextBox class, but changing any property of a TextBox control doesn’t affect the other two
controls. Classes are the blueprints on which objects are based. We use the same blueprint to build
multiple buildings with the same structural characteristics, but different properties (wall colors, doors,
and so on).
Objects are similar toWindows controls, except that they don’t have a visible interface. Controls
are instantiated when you place them on a form; classes are instantiated when you use a variable
of the same type — not when you declare the variable by using the Dim statement. To use a control,
you must make it part of the project by adding its icon to the Toolbox, if it’s not already there. To
use a class in your code, you must import the file that implements the class. (This is a Dynamic
Link Library, or DLL, file.) To manipulate a control from within your code, you call its properties
and methods. You do the same with classes. Finally, you program the various events raised by
the controls to interact with the users of your applications. Most classes don’t expose any events
because the user can’t interact with them, but some classes do raise events, which you can program
just as you program the events ofWindows controls.
Classes Combine Code with Data
Another way to view classes is to understand how they combine code and data. This simple
idea is the very essence of object-oriented programming. Data is data, and traditional procedural
languages allow you to manipulate data in any way. Meaningful data, however, is processed in
specific ways.
Let’s consider accounting data. You can add or subtract amounts to an account, sum similar
accounts (such as training and travel expenses), calculate taxes on certain account amounts, and
the like. Other types of processing may not be valid for this type of data. We never multiply the
amounts of two different accounts or calculate logarithms of account balances. These types of
processing are quite meaningful with different data, but not with accounting data.Petroutsos c10.tex V3 - 01/28/2008 1:32pm Page 352
352 CHAPTER 10 BUILDING CUSTOM CLASSES
Because the data itself determines to a large extent the type of processing that will take place
on the data, why not ‘‘package’’ the data along with the code for processing? Instead of simply
creating structures for storing our data, we also write the code to process them. The data and the
code are implemented in a single unit, a class, and the result is an object. After the class has been
built, we no longer write code for processing the data; we simply create objects of this type and
call their methods. To transfer an amount from one account to another, we call a method that
knows how to transfer the amount, and it also makes sure that the amount isn’t subtracted from
one account unless it has been added to the other account (and vice versa).
To better understand how classes combine code with data, let’s take a close look at a class
we’re all too familiar with, the Array class. The role of the array is to store sets of data. In addition
to holding data, the Array class also knows how to process data: how to retrieve an element,
how to extract a segment of the array, and even how to sort its elements. All these operations
require a substantial amount of code. The mechanics of storing data in the array and the code that
implements the properties and the methods of the array are hidden from you, the developer. You
can instruct the array to perform certain tasks by using simple statements.When you call the Sort
method, you’re telling the array to execute some code that will sort its elements. As a developer,
you don’t know how the data are stored in the array, or how the Sort method works. Classes
abstract many operations by hiding the implementation details, and developers can manipulate
arrays by calling methods. An instance of the Array class not only holds the elements thatmake up
an array, but also exposes the most common operation one would perform on arrays as methods.
Summing the logarithms of the elements of a numeric array is a specialized operation, and you
have to provide the code to implement it on your own. If you type System.Array., you will see a
list of all operations you can perform on an array.
In the following sections, you’ll learn how data and code coexist in a class, and how you can
manipulate the data through the properties and methods exposed by the class. In Chapter 3,
‘‘Programming Fundamentals,’’ you learned how to create Structures to store data. Classes are
similar to Structures, in that they represent custom data structures. In this chapter, we’ll take the
idea of defining custom data structures one step further, by adding properties and methods for
manipulating the custom data, something you can’t do with structures. Let’s start by building a
custom class and then using it in our code.
Building theMinimal Class
Our first example is the Minimal class; we’ll start with the minimum functionality class and keep
adding features to it. The name of the class can be anything — just make sure that it’s suggestive
of the class’s functionality.
A class might reside in the same file as a form, but it’s customary to implement custom classes
in a separate module, a Class module. You can also create a Class project, which contains one or
more classes. However, a class doesn’t run on its own, and you can’t test it without a form. You
can create a Windows application, add the class to it, and then test it by adding the appropriate
code to the form. After debugging the class, you can remove the test form and reuse the class with
any other project. Because the class is pretty useless outside the context of aWindows application,
in this chapter I use Windows applications and add a Class module in the same solution.
Start a new Windows project and name it SimpleClass (or open the sample project by that
name). Then create a new class by adding a Class item to your project. Right-click the project’s
name in the Solution Explorer window and choose Add  Class from the context menu. In the
dialog box that pops up, select the Class icon and enter a name for the class. Set the class’s name
to Minimal, as shown in Figure 10.1.Petroutsos c10.tex V3 - 01/28/2008 1:32pm Page 353
BUILDING THE MINIMAL CLASS 353
Figure 10.1
Adding a Class item to a
project
The code that implements the class resides in the Minimal.vb file, and we’ll use the existing
form to test our class. After you have tested and finalized the class’s code, you no longer need the
form and you can remove it from the project.
When you open the class by double-clicking its icon in the Project Explorer window, you will
see the following lines in the code window:
Public Class Minimal
End Class
If you’d rather create a class in the same file as the application’s form, enter the Class keyword
followed by the name of the class, after the existing End Class of the form’s code window. The
editor will insert the matching End Class for you. Insert a class’s definition in the form’s code
window if the class is specific to this form only and no other part of the application will use it. At
this point, you already have a class, even if it doesn’t do anything.
Switch back to the Form Designer, add a button to the test form, and insert the following code
in its Click event handler:
Dim obj1 As Minimal
Press Enter and type the name of the variable, obj1, followed by a period, on the following
line. You will see a list of the methods your class exposes already:
Equals
GetHashCode
GetType
ReferenceEqual
ToString
If you don’t see all of these members, switch to the All Members tab of the IntelliSense drop-
down box.
These methods are provided by the Common Language Runtime (CLR), and you don’t have
to implement them on your own (although you will probably have to provide a new, nongeneric
implementation for some of them). They don’t expose any real functionality; they simply reflectPetroutsos c10.tex V3 - 01/28/2008 1:32pm Page 354
354 CHAPTER 10 BUILDING CUSTOM CLASSES
the way VB handles all classes. To see the kind of functionality that these methods expose,
enter the following lines in the Button’s Click event handler and then run the application:
Dim obj1 As New Minimal
Debug.WriteLine(obj1.ToString)
Debug.WriteLine(obj1.GetType)
Debug.WriteLine(obj1.GetHashCode)
Dim obj2 As New Minimal
Debug.WriteLine(obj1.Equals(obj2))
Debug.WriteLine(Minimal.ReferenceEquals(obj1, obj2))
The following lines will be printed in the Output window:
SimpleClass.Minimal
SimpleClass.Minimal
18796293
False
False
The name of the object is the same as its type, which is all the information about your new class
that’s available to the CLR. Shortly you’ll see how you can implement your own ToString method
and return a more-meaningful string. The hash value of the obj1 variable is an integer value that
uniquely identifies the object variable in the context of the current application (it happens to be
18796293, but it is of no consequence).
The next line tells you that two variables of the same type are not equal. But why aren’t they
equal? We haven’t differentiated them at all, yet they’re different because they point to two dif-
ferent objects, and the compiler doesn’t know how to compare them. All it can do is figure out
whether the variables point to the same object. To understand how objects are compared, add the
following statement after the line that declares obj2:
obj2 = obj1
If you run the application again, the last two statements will print True in the Output window.
The Equals method compares the two objects and returns a True/False value. Because we haven’t
told it how to compare two instances of the class yet, it compares their references just like the
ReferenceEquals method. The ReferenceEquals method checks for reference equality; that is, it
returns True if both variables point to the same object (the same instance of the class). If you change
a property of the obj1 variable, the changes will affect obj2 as well, because both variables point
to the same object.We can’t modify the object because it doesn’t expose any members that we can
set to differentiate it from another object of the same type. We’ll get to that shortly.
Most classes expose a custom Equals method, which knows how to compare two objects of the
same type (two objects based on the same class). The custom Equals method usually compares
the properties of the two instances of the class and returns True if a set of basic properties (or all
of them) are the same. You’ll learn how to customize the default members of any class later in
this chapter.
Notice the name of the class: SimpleClass.Minimal. Within the current project, you can access
it as Minimal. Other projects can either import the Minimal class and access it asMinimal, or spec-
ify the complete name of the class, which is the name of the project it belongs to followed by the
class name.Petroutsos c10.tex V3 - 01/28/2008 1:32pm Page 355
BUILDING THE MINIMAL CLASS 355
Adding Code to the Minimal Class
Let’s add some functionality to our bare-bones class. We’ll begin by adding two trivial properties
and two methods to perform simple operations. The two properties are called strProperty (a
string) and dblProperty (a double). To expose these two members as properties, you can simply
declare them as Public variables. This isn’t the best method of implementing properties, but it
really doesn’t take more than declaring something as Public to make it available to code outside
the class. The following statement exposes the two properties of the class:
Public strProperty As String, dblProperty As Double
The two methods we’ll implement in our sample class are the ReverseString and Negate
Number methods. The first method reverses the order of the characters in strProperty and
returns the new string. The NegateNumber method returns the negative of dblProperty.They’re
two simple methods that don’t accept any arguments; they simply operate on the values of the
properties. Methods are exposed as Public procedures (functions or subroutines), just as proper-
ties are exposed as Public variables. Enter the function declarations of Listing 10.1 between the
Class Minimal and End Class statements in the class’s code window. (I’m showing the entire
listing of the class here.)
Listing 10.1: Adding a FewMembers to theMinimal Class
Public Class Minimal
Public strProperty As String, dblProperty As Double
Public Function ReverseString() As String
Return (StrReverse(strProperty))
End Function
Public Function NegateNumber() As Double
Return (-dblProperty)
End Function
End Class
Let’s test the members we’ve implemented so far. Switch back to your form and enter the lines
shown in Listing 10.2 in a new button’s Click event handler. The obj variable is of the Minimal
type and exposes the Public members of the class. You can set and read its properties, and call its
methods. In Figure 10.2, you see a few more members than the ones added so far; we’ll extend our
Minimal class in the following section. Your code doesn’t see the class’s code, just as it doesn’t see
any of the built-in classes’ code. You trust that the class knows what it is doing and does it right.
Listing 10.2: Testing theMinimal Class
Dim obj As New Minimal
obj.strProperty = ”ABCDEFGHIJKLMNOPQRSTUVWXYZ”
obj.dblProperty = 999999
Debug.WriteLine(obj.ReverseString)
Debug.WriteLine(obj.NegateNumber)Petroutsos c10.tex V3 - 01/28/2008 1:32pm Page 356
356 CHAPTER 10 BUILDING CUSTOM CLASSES
Figure 10.2
The members of the
class are displayed auto-
matically by the IDE, as
needed.
Every time you create a new variable of theMinimal type, you’re creating a new instance of the
Minimal class. The class’s code is loaded into memory only the first time you create a variable of
this type, but every time you declare another variable of the same type, a new set of variables is
created. This is called an instance of the class. The code is loaded once, but it can act on different sets
of variables. In effect, different instances of the class are nothing more than different sets of local
variables.
The New Keyword
The New keyword tells VB to create a new instance of the Minimal class. If you omit the New keyword,
you’re telling the compiler that you plan to store an instance of the Minimal class in the obj variable,
but the class won’t be instantiated. All the compiler can do is prevent you from storing an object of
any other type in the obj variable. You must still initialize the obj variable with the New keyword on
a separate line:
obj = New Minimal
It’s the New keyword that creates the object in memory. The obj variable simply points to this object.
If you omit the New keyword, a Null Reference exception will be thrown when the code attempts to
use the variable. This means that the variable is Nothing — it hasn’t been initialized yet. Even as you
work in the editor’s window, the name of the variable will be underlined and the following warning
will be generated: Variable ‘obj’ is used before it has been assigned a value. A null reference exception
could result at runtime. You can compile the code and run it if you want, but everything will proceed
as predicted: As soon as the statement that produced the warning is reached, the runtime exception
will be thrown.
Using Property Procedures
The strProperty and dblProperty properties will accept any value, as long as the type is correct
and the value of the numeric property is within the acceptable range. But what if the generic
properties were meaningful entities, such as email addresses, ages, or zip codes? We should be
able to invoke some code to validate the values assigned to each property. To do so, we implement
each property as a special type of procedure: the so-called Property procedure.Petroutsos c10.tex V3 - 01/28/2008 1:32pm Page 357
BUILDING THE MINIMAL CLASS 357
Properties are implemented with a special type of procedure that contains a Get and a Set
section (frequently referred to as the property’s getter and setter, respectively). The Set section
of the procedure is invoked when the application attempts to set the property’s value; the Get
section is invoked when the application requests the property’s value. The value passed to the
property is usually validated in the Set section and, if valid, is stored to a local variable. The same
local variable’s value is returned to the application when it requests the property’s value, from
the property’s Get section. Listing 10.3 shows what the implementation of an Age property would
look like.
Listing 10.3: Implementing Propertieswith Property Procedures
Private m Age As Integer
Property Age() As Integer
Get
Age = m Age
End Get
Set (ByVal value As Integer)
If value < 0 Or value >= 100 Then
MsgBox(”Age must be positive and less than 100”)
Else
m Age = value
End If
End Set
End Property
m Age is the local variable where the age is stored. When a statement such as the following
is executed in the application that uses your class, the Set section of the Property procedure is
invoked:
obj.Age = 39
Because the property value is valid, it is stored in the m Age local variable. Likewise, when
a statement such as the following one is executed, the Get section of the Property procedure is
invoked, and the value 39 is returned to the application:
Debug.WriteLine(obj.Age)
The value argument of the Set procedure represents the actual value that the calling code is
attempting to assign to the property. The m Age variable is declared as private because we don’t
want any code outside the class to access it directly. The Age property is, of course, Public, so that
other applications can set it.
Fields versus Properties
Technically, any variables that are declared as Public in a class are called fields. Fields behave just like
properties in the sense that you can assign values to them and read their values, but there’s a criti-
cal distinction between fields and properties: When a value is assigned to a field, you can’t validate
the value from within your code. If the value is of the wrong type, an exception will occur. PropertiesPetroutsos c10.tex V3 - 01/28/2008 1:32pm Page 358
358 CHAPTER 10 BUILDING CUSTOM CLASSES
should be implemented with a Property procedure, so you can validate their values, as you saw in
the preceding example. Not only that, but you can set other values from within your code. Consider
a class that represents a contract with a starting and ending date. Every time the user changes the
starting date, the code can adjust the ending date accordingly (which is something you can’t do with
fields). If the two dates were implemented as fields, users of the class could potentially specify an
ending date prior to the starting date.
Enter the Property procedure for the Age property in the Minimal class and then switch to
the form to test it. Open the button’s Click event handler and add the following lines to the
existing ones:
obj.Age = 39
Debug.WriteLine(”after setting the age to 39, age is ” &
obj.Age.ToString)
obj.Age = 199
Debug.WriteLine(”after setting the age to 199, age is ” &
obj.Age.ToString)
The value 39 will appear twice in the Output window, which means that the class accepts the
value 39. When the third statement is executed, a message box will appear with the
error’s description:
Age must be positive and less than 100
The value 39 will appear in the Output window again. The attempt to set the age to 199 failed,
so the property retains its previous value.
Throwing Exceptions
Our error-trapping code works fine, but what good is a message box displayed from within a
class? As a developer using the Minimal class in your code, you’d rather receive an exception and
handle it from within your code. So let’s change the implementation of the Age property a little.
The Property procedure for the Age property (Listing 10.4) throws an InvalidArgument exception
if an attempt is made to assign an invalid value to it. The InvalidArgument exception is one of
the existing exceptions, and you can reuse it in your code. Later in this chapter, you’ll learn how
to create and use custom exceptions.
Listing 10.4: Throwing an Exception fromwithin a Property Procedure
Private m Age As Integer
Property Age() As Integer
Get
Age = m Age
End Get
Set (ByVal value As Integer)
If value < 0 Or value >= 100 ThenPetroutsos c10.tex V3 - 01/28/2008 1:32pm Page 359
BUILDING THE MINIMAL CLASS 359
Dim AgeException As New ArgumentException()
Throw AgeException
Else
M Age = value
End If
End Set
End Property
You can test the revised property definition in your application; switch to the test form, and
enter the statements of Listing 10.5 in a new button’s Click event handler. (This is the code behind
the Handle Exceptions button on the test form.)
Listing 10.5: Catching the Age Property’s Exception
Dim obj As New Minimal
Dim userAge as Integer
UserAge = InputBox(”Please enter your age”)
Try
obj.Age = userAge
Catch exc as ArgumentException
MsgBox(”Can’t accept your value, ” & userAge.ToString & VbCrLf &
”Will continue with default value of 30”)
obj.Age = 30
End Try
This is a much better technique for handling errors in your class. The exceptions can be inter-
cepted by the calling application, and developers using your class can write robust applications by
handling the exceptions in their code. When you develop custom classes, keep in mind that you
can’t handle most errors from within your class because you don’t know how other developers
will use your class.
Handling Errors in a Class
When you design classes, keep in mind that you don’t know how another developer may use
them. In fact, you may have to use your own classes in a way that you didn’t consider when you
designed the class. A typical example is using an existing class with a web application. If your class
displays a message box, it will work fine as part of a Windows Forms application. In the context of a
web application, however, the message box will be displayed on the monitor of the server that hosts
the application, and no one will see it. As a result, the application will keep waiting for a response to a
message box before it continues; however, there’s no user to click the OK button in the message box,
because the code is executing on a server. Even if you don’t plan to use a custom class with a web
application, never interact with the user from within the class’s code. Make your code as robust as
you can, but don’t hesitate to throw exceptions for all conditions you can’t handle from within your
code (Figure 10.3). In general, a class’s code should detect abnormal conditions, but it shouldn’t
attempt to remedy them.Petroutsos c10.tex V3 - 01/28/2008 1:32pm Page 360
360 CHAPTER 10 BUILDING CUSTOM CLASSES
The application that uses your class may inform the user about an error condition and give the user a
chance to correct the error by entering new data, disable some options on the interface, and so on. As
a class developer, you can’t make this decision — another developer might prompt the user for
another value, and a sloppy developer might let his or her application crash (but this isn’t your
problem). To throw an exception from within your class’s code, call the Throw statement with an
Exception object as an argument. To play well with the Framework, you should try to use one of the
existing exceptions (and the Framework provides quite a few of them). You can also throw custom
exceptions by using a statement such as the following:
Throw New Exception(”your exception’s description”)
Figure 10.3
Raising an exception in
the class’s code
Implementing Read-Only Properties
Let’s make our class a little more complicated. Age is not usually requested on official docu-
ments, because it’s valid only for a year after filling out a questionnaire. Instead, you must furnish
your date of birth, from which your current age can be calculated at any time. We’ll add a BDate
property in our class and make Age a read-only property.
To make a property read-only, you simply declare it as ReadOnly and supply the code for the
Get procedure only. Revise the Age property’s code in the Minimal class, as seen in Listing 10.6.
Then enter the Property procedure from Listing 10.7 for the BDate property.
Listing 10.6: Implementing a Read-Only Property
Private m Age As Integer
ReadOnly Property Age() As Integer
Get
Age = m Age
End Get
End PropertyPetroutsos c10.tex V3 - 01/28/2008 1:32pm Page 361
BUILDING THE MINIMAL CLASS 361
Listing 10.7: The BDate Property
Private m BDate As DateTime
Private m Age As Integer
Property BDate() As DateTime
Get
BDate = m BDate
End Get
Set(ByVal value As Date)
If Not IsDate(value) Then
Dim DataTypeException As
New Exception(”Invalid date value”)
Throw DataTypeException
End If
If value > Now() Or
DateDiff(DateInterval.Year, value, Now()) >= 100 Then
Dim AgeException As New Exception
(”Can’t accept the birth date you specified”)
Throw AgeException
Else
m BDate = value
m Age = DateDiff(DateInterval.Year, value, Now())
End If
End Set
End Property
As soon as you enter the code for the revised Age property, two error messages will appear
in the Error List window. The code in the application’s form is attempting to set the value of a
read-only property, and the editor produces the following error message twice: Property ‘Age’ is
‘ReadOnly.’ As you probably figured out, we must set the BDate property in the code, instead
of the Age property. The two errors are the same, but they refer to two different statements that
attempt to set the Age property.
There are two types of errors that can occur while setting the BDate property: an invalid date
or a date that yields an unreasonable age. First, the code of the BDate property makes sure that
the value passed by the calling application is a valid date. If not, it throws an exception. If the
value variable is a valid date, the code calls the DateDiff() function, which returns the difference
between two dates in a specified interval— in our case, years. The expression DateInterval.Year
is the name of a constant, which tells the DateDiff() function to calculate the difference between
the two dates in years. You don’t have to memorize the constant names — you simply select them
from a list as you type.
So, the code checks the number of years between the date of birth and the current date. If it’s
negative (which means that the person hasn’t been born yet) or more than 100 years (we’ll assume
that people over 100 will be treated as being 100 years old), it rejects the value. Otherwise, it sets
the value of the m BDate local variable and calculates the value of the m Age local variable.
Calculating Property Values on the Fly
There’s still a serious flaw in the implementation of the Age property. Can you see it? The person’s
age is up-to-date the moment the birth date is entered, but what if we read it back from a file orPetroutsos c10.tex V3 - 01/28/2008 1:32pm Page 362
362 CHAPTER 10 BUILDING CUSTOM CLASSES
database three years later? It will still return the original value, which is no longer the correct age.
The Age property’s value shouldn’t be stored anywhere; it should be calculated from the person’s
birth date as needed. If we avoid storing the age to a local variable and calculate it on the fly,
users will always see the correct age. Revise the Age property’s code to match Listing 10.8, which
calculates the difference between the date of birth and the current date, and returns the correct
person’s age every time it’s called.
Listing 10.8: A Calculated Property
ReadOnly Property Age() As Integer
Get
Age = Convert.ToInt32(DateDiff(DateInterval.Year, m BDate , Now()))
End Get
End Property
Notice also that you no longer need the m Age local variable because the age is calculated on
the fly when requested, so remove its declaration from the class. As you can see, you don’t always
have to store property values to local variables. A property that returns the number of files in a
directory, for example, also doesn’t store its value in a local variable. It retrieves the requested
information on the fly and furnishes it to the calling application. By the way, the calculations
might still return a negative value if the user has changed the system’s date, but this is a rather
far-fetched scenario.
You can implement write-only properties with the WriteOnly keyword and a Set section only,
but write-only properties are rarely used (in my experience, only for storing passwords).
Our Minimal class is no longer so minimal. It exposes some functionality, and you can easily
add more. Add properties for name, profession, and income, and add methods to calculate insur-
ance rates based on a person’s age and anything you can think of. Experiment with a few custom
members, add the necessary validation code in your Property procedures, and you’ll soon find
out that building and reusing custom classes is a simple and straightforward process. Of course,
there’s a lot more to learn about classes, but you already have a good understanding of the way
classes combine code with data.
Customizing Default Members
As you recall, when you created the Minimal class for the first time, before adding any code,
the class already exposed a few members— the default members, such as the ToString method
(which returns the name of the class) and the Equals method (which compares two objects for ref-
erence equality). You can (and should) provide your custom implementation for these members;
this is what we’re going to do in this section.
Customizing the ToString Method
The custom ToString method is implemented as a Public function, and it must override the
default implementation. The implementation of a custom ToString method is shown next:
Public Overrides Function ToString() As String
Return ”The infamous Minimal class”
End FunctionPetroutsos c10.tex V3 - 01/28/2008 1:32pm Page 363
BUILDING THE MINIMAL CLASS 363
As soon as you enter the keyword Overrides, the editor will suggest the names of the three
members you can override: ToString, Equals,and GetHashCode. Select the ToString method,
and the editor will insert a default implementation for you. The default implementation returns
the string MyBase.ToString. This is the default implementation. (You’ll see later in this chapter
what the MyBase keyword is; it basically references the default class implementation.) Just
replace the statement inserted by the editor with the one shown in the preceding statement. It’s
that simple.
The Overrides keyword tells the compiler that this implementation overwrites the default
implementation of the class. The original method’s code isn’t exposed, and you can’t revise it. The
Overrides keyword tells the compiler to ‘‘hide’’ the original implementation and use your custom
ToString method instead. After you override a method in a class, the application using the class
can no longer access the original method. Ours is a simple method, but you can return any string
you can build in the function. For example, you can incorporate the value of the BDate property
in the string:
Return(”MINIMAL: ” & m BDate.ToShortDateString)
The value of the local variable m BDate is the value of the BDate property of the current instance
of the class. Change the BDate property, and the ToString method will return a different string.
When called through different variables, the ToString method will report different values.
Let’s say that you created and initialized two instances of theMinimal class by using the following
statements:
Dim obj1 As New Minimal()
Obj1.Bdate = #1/1/1963#
Dim obj2 As New Minimal()
Obj2.Bdate = #12/31/1950#
Debug.WriteLine(obj1.ToString)
Debug.WriteLine(obj2.ToString)
The last two statements will print the following lines in the Output window:
MINIMAL: 1963-01-01
MINIMAL: 1950-12-31
Customizing the Equals Method
The Equals method exposed by most of the built-in objects can compare values, not references.
Two Rectangle objects, for example, are equal if their dimensions and origins are the same. The
following two rectangles are equal:
Dim R1 As New Rectangle(0, 0, 30, 60)
Dim R2 As New Rectangle
R2.X = 0
R2.Y = 0
R2.Width = 30
R2.Height = 60
If R1.Equals(R2) Then
MsgBox(”The two rectangles are equal”)
End IfPetroutsos c10.tex V3 - 01/28/2008 1:32pm Page 364
364 CHAPTER 10 BUILDING CUSTOM CLASSES
If you execute these statements, a message box confirming the equality of the two objects
will pop up. The two variables point to different objects (that is, different instances of the same
class), but the two objects are equal, because they have the same origin and same dimensions. The
Rectangle class provides its own Equals method, which knows how to compare two Rectangle
objects. If your class doesn’t provide a custom Equals method, all the compiler can do is com-
pare the objects referenced by the two variables of the specific type. In the case of our Minimal
class, the Equals method returns True if the two variables point to the same object (which is the
same instance of the class). If the two variables point to two different objects, the default Equals
method will return False, even if the two objects are equal.
You’re probably wondering what makes two objects equal. Is it all their properties or perhaps
some of them? Two objects are equal if the Equals method says so. You should compare the objects
in a way that makes sense, but you’re in no way limited as to how you do this. In a very specific
application, you might decide that two rectangles are equal because they have the same area, or
perimeter, regardless of their dimensions and origin, and override the Rectangle object’s Equals
method. In theMinimal class, for example, youmight decide to compare the birth dates and return
True if they’re equal. Listing 10.9 is the implementation of a possible custom Equals method for
the Minimal class.
Listing 10.9: ACustomEqualsMethod
Public Overrides Function Equals(ByVal obj As Object) As Boolean
Dim O As Minimal = CType(obj, Minimal)
If O.BDate = m BDate Then
Equals = True
Else
Equals = False
End If
End Function
Notice that the Equals method is prefixed with the Overrides keyword, which tells the
compiler to use our custom Equals method in the place of the original one. To test the new Equals
method, place a new button on the form and insert the statements of Listing 10.10 in its Click
event handler.
Listing 10.10: Testing the CustomEqualsMethod
Dim O1 As New Minimal
Dim O2 As New Minimal
O1.BDate = #3/1/1960#
O2.BDate = #3/1/1960#
O1.strProperty = ”object1”
O2.strProperty = ”OBJECT2”
If O1.Equals(O2) Then
MsgBox(”They’re equal”)
End IfPetroutsos c10.tex V3 - 01/28/2008 1:32pm Page 365
BUILDING THE MINIMAL CLASS 365
If you run the application, you’ll see the message confirming that the two objects are equal,
despite the fact that their strProperty properties were set to different values. The BDate property
is the same, and this is the only setting that the Equals method examines. So, it’s up to you to
decide which properties fully and uniquely identify an object and to use these properties to deter-
mine when two objects are equal. In a class that represents persons, you’d probably use Social
Security numbers, or a combination of names and birth dates. In a class that represents cars, you’d
use the maker, model, and year, and so on.
Know What You Are Comparing
The Equals method shown in Listing 10.10 assumes that the object you’re trying to compare to
the current instance of the class is of the same type. Because you can’t rely on developers to catch
all their mistakes, you should know what you’re comparing before you attempt to perform the
comparison. A more-robust implementation of the Equals method is shown in Listing 10.11. This
implementation tries to convert the argument of the Equals method to an object of the Minimal
type and then compares it to the current instance of the Minimal class. If the conversion fails, an
InvalidCastException is thrown and no comparison is performed.
Listing 10.11: AMore-Robust EqualsMethod
Public Overrides Function Equals(ByVal obj As Object) As Boolean
Dim O As New Minimal()
Try
O = DirectCast(obj, Minimal)
Catch typeExc As InvalidCastException
Throw typeExc
Exit Function
End Try
If O.BDate = m BDate Then
Equals = True
Else
Equals = False
End If
End Function
The Is Operator
The equals ( = ) operator can be used in comparing all built-in objects. The following statement is
quite valid, as long as the R1 and R2 variables were declared of the Rectangle type:
If R1 = R2 Then
MsgBox(”The two rectangles are equal”)
End If
This operator, however, can’t be used with the Minimal custom class. Later in this chapter,
you’ll learn how to customize operators in your class. In the meantime, you can use only the
Is operator, which compares for reference equality (whether the two variables reference thePetroutsos c10.tex V3 - 01/28/2008 1:32pm Page 366
366 CHAPTER 10 BUILDING CUSTOM CLASSES
same object), and the Equals method. If the two variables R1 and R2 point to the same object, the
following statement will return True:
If obj1 Is obj2 Then
MsgBox(”The two variables reference the same object”)
End If
The Is operator tells you that the two variables point to a single object. There’s no comparison
here; the compiler simply figures out whether they point to same object in memory. It will return
True if a statement such as the following has been executed before the comparison:
obj2 = obj1
If the Is operator returns True, there’s only one object in memory and you can set its properties
through either variable.
Custom Enumerations
Let’s add a little more complexity to our class. Because we’re storing birth dates to our custom
objects, we can classify persons according to their age. Most BASIC developers will see an oppor-
tunity to use constants here. Instead of using constants to describe the various age groups, we’ll
use an enumeration with the following group names:
Public Enum AgeGroup
Infant
Child
Teenager
Adult
Senior
Overaged
End Enum
These statements must appear outside any procedure in the class, and we usually place them
at the beginning of the file, right after the declaration of the class. Public is an access modifier (we
want to be able to access this enumeration from within the application that uses the class). Enum
is a keyword: It specifies the beginning of the declaration of an enumeration and it’s followed by
the enumeration’s name. The enumeration itself is a list of integer values, each one mapped to a
name. In our example, the name Infant corresponds to 0, the name Child corresponds to 1, and
so on. The list of the enumeration’s members ends with the End Enum keyword. You don’t really
care about the actual values of the names because the very reason for using enumerations is to
replace numeric constants with more-meaningful names. You’ll see shortly how enumerations are
used both in the class and the calling application.
As you already know, the Framework uses enumerations extensively, and this is how you
can add an enumeration to your custom class. You should provide an enumeration for any prop-
erty with a relatively small number of predetermined settings. The property’s type should be the
name of the enumeration, and the editor will open a drop-down box with the property’s settings
as needed.
Now add to the class the GetAgeGroup method (Listing 10.12), which returns the name of the
age group to which the person represented by an instance of the Minimal class belongs. The name
of the group is a member of the AgeGroup enumeration.Petroutsos c10.tex V3 - 01/28/2008 1:32pm Page 367
BUILDING THE MINIMAL CLASS 367
Listing 10.12: Using an Enumeration
Public Function GetAgeGroup() As AgeGroup
Select Case m Age
Case Is < 3 : Return (AgeGroup.Infant)
Case Is < 10 : Return (AgeGroup.Child)
Case Is < 21 : Return (AgeGroup.Teenager)
Case Is < 65 : Return (AgeGroup.Adult)
Case Is < 100 : Return (AgeGroup.Senior)
Case Else : Return (AgeGroup.Overaged)
End Select
End Function
The GetAgeGroup method returns a value of the AgeGroup type. Because the AgeGroup enu-
meration was declared as Public, it’s exposed to any application that uses the Minimal class. Let’s
see how we can use the same enumeration in our application. Switch to the form’s code window,
add a new button, and enter the statements from Listing 10.13 in its event handler.
Listing 10.13: Using the Enumeration Exposed by the Class
Protected Sub Button1 Click(...)
Handles Button1.Click
Dim obj As Minimal
obj = New Minimal()
Try
obj.BDate = InputBox(”Please Enter your birthdate”)
Catch ex As ArgumentException
MsgBox(ex.Message)
Exit Sub
End Try
Debug.WriteLine(obj.Age)
Dim discount As Single
If obj.GetAgeGroup = Minimal.AgeGroup.Infant Or
obj.GetAgeGroup = Minimal.AgeGroup.Child Then discount = 0.4
If obj.GetAgeGroup = Minimal.AgeGroup.Senior Then discount = 0.5
If obj.GetAgeGroup = Minimal.AgeGroup.Teenager Then discount = 0.25
MsgBox(”You age is ” & obj.Age.ToString &
” and belong to the ” &
obj.GetAgeGroup.ToString &
” group” & vbCrLf & ”Your discount is ” &
Format(discount, ”Percent”))
End Sub
This routine calculates discounts based on the person’s age. Notice that we don’t use numeric
constants in our code, just descriptive names. Moreover, the possible values of the enumeration
are displayed in a drop-down list by the IntelliSense feature of the IDE as needed (Figure 10.4),Petroutsos c10.tex V3 - 01/28/2008 1:32pm Page 368
368 CHAPTER 10 BUILDING CUSTOM CLASSES
and you don’t have to memorize them or look them up as you would with constants. I’ve used
an implementation with multiple If statements in this example, but you can perform the same
comparisons by using a Select Case statement.
Figure 10.4
The members of an enu-
meration are displayed
automatically in the IDE
as you type.
You could also call the GetAgeGroup method once, store its result to a variable, and then use
this variable in the comparisons. This approach is slightly more efficient, because you don’t have to
call the member of the class repeatedly. The variable, as you can guess, should be of the AgeGroup
type. Here’s an alternate code of the statements of Listing 10.13 using a temporary variable, the
grp variable, and a Select Case statement:
Dim grp As AgeGroup = obj.GetAgeGroup
Select Case grp
Case Minimal.AgeGroup.Infant, Minimal.AgeGroup.Child ...
Case Minimal.AgeGroup.Teenager ...
Case Minimal.AgeGroup.Senior ...
Case Else
End Select
You’ve seen the basics of working with custom classes in a VB application. Let’s switch to a
practical example that demonstrates not only the use of a real-world class, but also how classes
can simplify the development of a project.
VB 2008 at Work: The Contacts Project
In Chapter 7, ‘‘Working with Forms,’’ I discussed briefly the Contacts application. This application
uses a custom Structure to store the contacts and provides four navigational buttons to allow
userstomovetothefirst,last,previous,and next contact. Now that you have learned how to
program the ListBox control and how to use custom classes in your code, we’ll revise the Contacts
application. First, we’ll implement a class to represent each contact. The fields of each contact
(company and contact names, addresses, and so on) will be implemented as properties and they
will be displayed in the TextBox controls on the form.
We’ll also improve the user interface of the application. Instead of the rather simplistic nav-
igational buttons, we’ll place all the company names in a sorted ListBox control. The user can
easily locate the desired company and select it from the list to view the fields of the selected com-
pany. The editing buttons at the bottom of the form work as usual, but we no longer need the
navigational buttons. Figure 10.5 shows the revised Contacts application.Petroutsos c10.tex V3 - 01/28/2008 1:32pm Page 369
BUILDING THE MINIMAL CLASS 369
Figure 10.5
The interface of the
Contacts application is
based on the ListBox
control.
Make a copy of the Contacts folder from Chapter 7 into a new folder. First, delete the decla-
ration of the Contact structure and add a class to the project. Name the new class Contact and
enter the code from Listing 10.14 into it. The names of the private members of the class are the
same as the actual property names, and they begin with an underscore. (This is a good convention
that lets you easily distinguish whether a variable is private, and the property value it stores.) The
implementation of the properties is trivial, so I’m not showing the code for all of them.
Listing 10.14: The Contact Class
<Serializable()> Public Class Contact
Private companyName As String
Private contactName As String
Private address1 As String
Private address2 As String
Private city As String
Private state As String
Private zip As String
Private tel As String
Private email As String
Private URL As String
Property CompanyName() As String
Get
CompanyName = companyName
End Get
Set(ByVal value As String)
If value Is Nothing Or value = ”” Then
Throw New Exception(”Company Name field can’t be empty”)
Exit Property
End IfPetroutsos c10.tex V3 - 01/28/2008 1:32pm Page 370
370 CHAPTER 10 BUILDING CUSTOM CLASSES
companyName = value
End Set
End Property
Property ContactName() As String
Get
ContactName = contactName
End Get
Set(ByVal value As String)
contactName = value
End Set
End Property
Property Address1() As String
Get
Address1 = address1
End Get
Set(ByVal value As String)
address1 = value
End Set
End Property
Property Address2() As String
...
End Property
Property City() As String
...
End Property
Property State() As String
...
End Property
Property ZIP() As String
...
End Property
Property tel() As String
...
End Property
Property EMail() As String
Get
EMail = email
End Get
Set(ByVal value As String)
If value.Contains(”@”) Or value.Trim.Length = 0 Then
email = Value
Else
Throw New Exception(”Invalid e-mail address!”)Petroutsos c10.tex V3 - 01/28/2008 1:32pm Page 371
BUILDING THE MINIMAL CLASS 371
End If
End Set
End Property
Property URL() As String
Get
URL = URL
End Get
Set(ByVal value As String)
URL = value
End Set
End Property
Overrides Function ToString() As String
If contactName = ”” Then
Return companyName
Else
Return companyName & vbTab & ”(” & contactName & ”)”
End If
End Function
Public Sub New()
MyBase.New()
End Sub
Public Sub New(ByVal CompanyName As String,
ByVal LastName As String, ByVal FirstName As String)
MyBase.New()
Me.ContactName = LastName & ”, ” & FirstName
Me.CompanyName = CompanyName
End Sub
Public Sub New(ByVal CompanyName As String)
MyBase.New()
Me.CompanyName = CompanyName
End Sub
End Class
The first thing you’ll notice is that the class’s definition is prefixed by the <Serializable()>
keyword. The topic of serialization is discussed in Chapter 16, ‘‘XML and Object Serialization,’’
but for now all you need to know is that the .NET Framework can convert objects to a text or
binary format and then store them in files. Surprisingly, this process is quite simple; as you will
see, we’ll be able to dump an entire collection of Contact objects to a file with a single statement.
The <Serializable()> keyword is an attribute of the class, and (as you will see later in this
book) there are more attributes you can use with your classes — or even with your methods. The
most prominent method attribute is the <WebMethod> attribute, which turns a regular function
into a web method.
The various fields of the Contact structure are now properties of the Contact class. The imple-
mentation of the properties is trivial except for the CompanyName and EMail properties, whichPetroutsos c10.tex V3 - 01/28/2008 1:32pm Page 372
372 CHAPTER 10 BUILDING CUSTOM CLASSES
contain some validation code. The Contact class requires that the CompanyName property have a
value; if it doesn’t, the class throws an exception. Likewise, the EMail property must contain the
symbol @.Finally,theclassprovidesitsown ToString method, which returns the name of the
company followed by the contact name in parentheses.
The ListBox control, in which we’ll store all contacts, displays the value returned by the object’s
ToString method, which is why you have to provide your own implementation of the method to
describe each contact. The company name should be adequate, but if there are two companies by
the same name, you can use another field to differentiate them. I used the contact name, but you
can use any of the other properties (the URL would be a good choice).
The ListBox displays a string, but it stores the object itself. In essence, it’s used not only as a nav-
igational tool, but also as a storage mechanism for our contacts. Now, we must change the code of
the main form a little. Start by removing the navigational buttons; we no longer need them. Their
function will be replaced by a few lines of code in the ListBox control’s SelectedIndexChanged
event. Every time the user selects another item on the list, the statements shown in Listing 10.15
display the contact’s properties in the various TextBox controls on the form.
Listing 10.15: Displaying the Fields of the Selected Contact Object
Private Sub ListBox1 SelectedIndexChanged(...)
Handles ListBox1.SelectedIndexChanged
currentContact = ListBox1.SelectedIndex
ShowContact()
End Sub
The ShowContact() subroutine reads the object stored at the location specified by the cur-
rentContact variable and displays its properties in the various TextBox controls on the form. The
TextBox controls are normally read-only, except when editing a contact. This action is signaled by
clicking the Edit or the Add button on the form.
When a new contact is added, the code reads its fields from the controls on the form, creates
a new Contact object, and adds it to the ListBox control. When a contact is edited, a new Contact
object replaces the currently selected object on the control. The code is similar to the code of the
Contacts application. I should mention that the ListBox control is locked while a contact is being
added or edited, because it doesn’t make sense to select another contact at that time.
Adding, Editing, and Deleting Contacts
To delete a contact (Listing 10.16), we simply remove the currently selected object from the ListBox
control. In addition, we must select the next contact on the list (or the first contact if the deleted
one was last in the list).
Listing 10.16: Deleting an Object fromthe ListBox
Private Sub bttnDelete Click(...) Handles bttnDelete.Click
If currentContact > -1 Then
ListBox1.Items.RemoveAt(currentContact)
currentContact = ListBox1.Items.Count - 1
If currentContact = -1 Then
ClearFields()Petroutsos c10.tex V3 - 01/28/2008 1:32pm Page 373
BUILDING THE MINIMAL CLASS 373
MsgBox(”There are no more contacts”)
Else
ShowContact()
End If
Else
MsgBox(”No current contacts to delete”)
End If
End Sub
When you add a new contact, the following code is executed in the Add button’s Click event
handler:
Private Sub bttnAdd Click(...) Handles bttnAdd.Click
adding = True
ClearFields()
HideButtons()
ListBox1.Enabled = False
End Sub
The controls are cleared in anticipation of the new contact’s fields, and the adding variable is set
to True. The OK button is clicked to end either the addition of a new record or an edit operation.
The code behind the OK button is shown in Listing 10.17.
Listing 10.17: Committing a New or Edited Record
Private Sub bttnOK Click(...) Handles bttnOK.Click
If SaveContact() Then
ListBox1.Enabled = True
ShowButtons()
End If
End Sub
As you can see, the same subroutine handles both the insertion of a new record and the editing
of an existing one. All the work is done by the SaveContact() subroutine, which is shown in
Listing 10.18.
Listing 10.18: The SaveContact() Subroutine
Private Function SaveContact() As Boolean
Dim contact As New Contact
Try
contact.CompanyName = txtCompany.Text
contact.ContactName = txtContact.Text
contact.Address1 = txtAddress1.Text
contact.Address2 = txtAddress2.Text
contact.City = txtCity.Text
contact.State = txtState.Text
contact.ZIP = txtZIP.TextPetroutsos c10.tex V3 - 01/28/2008 1:32pm Page 374
374 CHAPTER 10 BUILDING CUSTOM CLASSES
contact.tel = txtTel.Text
contact.EMail = txtEMail.Text
contact.URL = txtURL.Text
Catch ex As Exception
MsgBox(ex.Message)
Return False
End Try
If adding Then
ListBox1.Items.Add(contact)
Else
ListBox1.Items(currentContact) = contact
End If
Return True
End Function
The SaveContact() function uses the adding variable to distinguish between an add and
an edit operation, and either adds the new record to the ListBox control or replaces the current
item in the ListBox with the values on the various controls. Because the ListBox is sorted, new
contacts are automatically inserted in the correct order. If an error occurs during the operation,
the SaveContact() function returns False to alert the calling code that the operation failed (most
likely because one of the assignment operations caused a validation error in the class’s code).
The last operation of the application is the serialization and deserialization of the items in the
ListBox control. Serialization is the process of converting an object to a stream of bytes for storing
to a disk file, and deserialization is the opposite process. To serialize objects, we first store them
into an ArrayList object, which is a dynamic array that stores objects and can be serialized as a
whole. Likewise, the disk file is deserialized into an ArrayList to reload the persisted data back to
the application; then each element of the ArrayList is moved to the Items collection of the ListBox
control. ArrayLists and other Framework collections are discussed in Chapter 14, ‘‘Storing Data
in Collections,’’ and object serialization is discussed in Chapter 16. You can use these features
to test the application and examine the corresponding code after you read about ArrayLists and
serialization. I’ll discuss the code of the Load and Save operations of the Contacts sample project
in Chapter 16.
Making the Most of the ListBox Control
This section’s sample application demonstrates an interesting technique for handling a set of data at
the client. We usually need an efficient mechanism to store data at the client, where all the process-
ing takes place — even if the data comes from a database. In this example, we used the ListBox con-
trol, because each item of the control can be an arbitrary object. Because the control displays the
string returned by the object’s ToString method, we’re able to customize the display by providing
our own implementation of the ToString method. As a result, we’re able to use the ListBox control
both as a data-storage mechanism and as a navigational tool. As long as the strings displayed on the
control are meaningful descriptions of the corresponding objects and the control’s items are sorted,
the ListBox control can be used as an effective navigational tool. If you have toomany items to display
on the control, you should also provide a search tool to help users quickly locate an item in the list,
without having to scroll up and down a long list of items. Review the ListBoxFind project of Chapter
6 for information on searching the contents of the ListBox control.Petroutsos c10.tex V3 - 01/28/2008 1:32pm Page 375
BUILDING THE MINIMAL CLASS 375
When data are being edited, you have to cope with another possible problem. The user may edit the
data for hours and forget to save the edits every now and then. If the computer (or, even worse,
the application) crashes, a lot of work will be wasted. Sure the application provides a Save command,
but you should always try to protect users from their mistakes. It would be nice if you could save the
data to a temporary file every time the user edits or adds an item to the list. This way, if the computer
crashes, users won’t lose their edits. When the application starts, it should automatically detect the
presence of the temporary file and reload it. Every time the user saves the data by using the applica-
tion’s Save command, or terminates the application, the temporary file should be removed.
Object Constructors
Let’s switch to a few interesting topics in programming with objects. Objects are instances of
classes, and classes are instantiated with the New keyword. The New keyword can be used with
a number of arguments, which are the initial values of some of the object’s basic properties. To
construct a rectangle, for example, you can use these two statements:
Dim shape1 As Rectangle = New Rectangle()
shape1.Width = 100
shape1.Height = 30
or the following one:
Dim shape1 As Rectangle = New Rectangle(100, 30)
The objects in the Minimal class can’t be initialized to specific values of their properties and
they expose the simple form of the New constructor — the so-called parameterless constructor.
Every class has a parameterless constructor, even if you don’t specify it. You can implement param-
eterized constructors, which allow you to pass arguments to an object as you declare it. These
arguments are usually the values of the object’s basic properties. Parameterized constructors don’t
pass arguments for all the properties of the object; they expect only enough parameter values to
make the object usable.
Parameterized constructors are implemented via Public subroutines that have the name New.
You can have as many overloaded forms of the New() subroutine as needed. Most of the built-in
classes provide a parameterless constructor, but the purists of OOP will argue against param-
eterless constructors. Their argument is that you shouldn’t allow users of your class to create
invalid instances of it. A class for describing customers, for example, should expose at least a Name
property. A class for describing books should expose a Title and an ISBN property. If the corre-
sponding constructor requires that these properties be specified before creating an instance of the
class, you’ll never create objects with invalid data.
Let’s add a parameterized constructor to our Contact class. Each contact should have at least a
name; here’s a parameterized constructor for the Contact class:
Public Sub New(ByVal CompanyName As String)
MyBase.New()
Me.CompanyName = CompanyName
End Sub
The code is trivial, with the exception of the statement that calls the MyBase.New() subroutine.
MyBase is an object that lets you access the members of the base class (a topic that’s discussedPetroutsos c10.tex V3 - 01/28/2008 1:32pm Page 376
376 CHAPTER 10 BUILDING CUSTOM CLASSES
in detail later in this chapter). The reason you must call the New method of the base class is that
the base class might have its own constructor, which can’t be called directly. You must always
insert this statement in your constructors to make sure that any initialization tasks that must be
performed by the base class will not be skipped.
The Contact class’s constructor accepts a single argument: the company name (this property
can’t be a blank string). Another useful constructor for the same class accepts two additional
arguments, the contact’s first and last names, as follows:
Public Sub New(ByVal CompanyName As String,
ByVal LastName As String, ByVal FirstName As String)
MyBase.New()
Me.ContactName = LastName & ”, ” & FirstName
Me.CompanyName = CompanyName
End Sub
With the two parameterized constructors in place, you can create new instances of the Contact
class by using statements such as the following:
Dim contact1 As New Contact(”Around the Horn”)
Or the following:
Dim contact1 As New Contact(”Around the Horn”, ”Hardy”, ”Thomas”)
Notice the lack of the Overloads (or Overrides) keyword. Constructors can have multiple
forms and don’t require the use of Overloads — just supply as many implementations of the
New() subroutine as you need.
One last, but very convenient technique to initialize objects was introduced with Visual Basic
2008. This technique allows you to supply values for as many properties of the new object as you
wish, using the With keyword. The following statements create two new instances of the Person
class, and they initialize each one differently:
Dim P1 As New Person With
{.LastName = ”Doe”, .FirstName = ”Joe”})
Dim P2 As New Person With
{.LastName = ”Doe”, .Email = ”Doe@xxx.com”})
This syntax allows you to quickly initialize new objects, regardless of their constructors; in
effect, you can create your own constructor for any class. This technique will be handy when
combining object initialization with other statements, such as in the following example, which
adds a new object to a list:
Persons.Add(New Person With {.LastName = ”Doe”, .FirstName = ”Joe”})
Persons.Add(New Person With {.LastName = ”Doe”})
Using the SimpleClass in Other Projects
The projects we built in this section are Windows applications that contain a Class module. The
class is contained within the project, and it’s used by the project’s main form. What if you want to
use this class in another project?Petroutsos c10.tex V3 - 01/28/2008 1:32pm Page 377
BUILDING THE MINIMAL CLASS 377
First, you must change the type of the project. AWindows project can’t be used as a component
in another project. Right-click the SimpleClass project and choose Properties. In the project’s Prop-
erty Pages dialog box, switch to the Application tab, locate the Application Type drop-down list,
and change the project’s type from Windows Forms Application to Class Library, as shown in
Figure 10.6. Then close the dialog box. When you return to the project, right-click the TestForm
and select Exclude From Project. A class doesn’t have a visible interface, and there’s no reason to
include the test form in your project.
Figure 10.6
Setting a project’s prop-
erties through the Prop-
erty Pages dialog box
From the main menu, choose Build  Build SimpleClass. This command will compile the
SimpleClass project and create a DLL file (the file that contains the class’s code and the file you
must use in any project that needs the functionality of the SimpleClass class). The DLL file will be
created in the \bin\Release folder under the project’s folder.
Let’s use the SimpleClass.dll file in another project. Start a new Windows application, open
the Project menu, and add a reference to the SimpleClass. Choose Project  Add Reference and
switch to the Projects tab in the dialog box that appears. Click the Browse button and locate
the SimpleClass.dll file (see Figure 10.7). Select the name of the file and click OK to close the
dialog box.
The SimpleClass component will be added to the project. You can now declare a variable of the
SimpleClass.Minimal type and call its properties and methods:
Dim obj As New SimpleClass.Minimal
obj.BDate = #10/15/1992#
obj.property2 = 5544
MsgBox(obj.Negate())
If you want to keep testing the SimpleClass project, add the TestForm to the original project
(right-click the project’s name, choose Add  Add Existing Item, and select the TestForm in the
project’s folder). Change the project’s type back to Windows Forms Application and then change
its configuration from Release to Debug.Petroutsos c10.tex V3 - 01/28/2008 1:32pm Page 378
378 CHAPTER 10 BUILDING CUSTOM CLASSES
Figure 10.7
Adding a reference to an
existing class to a new
project
Firing Events
In addition to methods and properties, classes can also fire events. It’s possible to raise events
from within your classes, although not quite as common. Controls have many events because they
expose a visible interface and the user interacts through this interface (clicks, drags and drops, and
so on). But classes can also raise events. Class events can come from three different sources:
Progress events A class might raise an event to indicate the progress of a lengthy process
or indicate that an internal variable or property has changed value. The PercentDone event is
a typical example. A process that takes a while to complete reports its progress to the calling
application with this event, which is fired periodically. These events, which are called progress
events,arethemostcommontypeofclassevents.
Time events Time events are based on a timer. They’re not very common, but you can imple-
ment alarms, job schedulers, and similar applications. You can set an alarm for a specific time
or an alarm that will go off after a specified interval.
External events External events, such as the completion of an asynchronous operation, can
also fire events. A class might initiate a file download and notify the application when the file
arrives.
To fire an event from within a class, you must do the following:
1. First you must declare the event and its signature in your class. The declaration must
appear in the form, not in any procedure. A simple event, with no arguments, should be
declared as follows (ShiftEnd is the name of the event— an event that signals the end of a
shifteveryeighthours):
Public Event ShiftEnd()Petroutsos c10.tex V3 - 01/28/2008 1:32pm Page 379
BUILDING THE MINIMAL CLASS 379
2. Fire the event from within your class’s code with the RaiseEvent method:
RaiseEvent ShiftEnd()
3. That’s all as far as the class is concerned.
4. The application that uses the custom class must declare it with the WithEvents keyword.
Otherwise, it will still be able to use the class’s methods and properties, but the events
raised by the class will go unnoticed. The following statement creates an instance of the
class and listens for any event:
Dim WithEvents obj As New Minimal
5. Finally, the calling application must provide a handler for the specific event. Because the
class was declared with the WithEvents keyword, its name will appear in the list of objects
in the editor window and its ShiftEnd event will appear in the list of events (Figure 10.8).
Insert the code you want to handle this event in the procedure obj.ShiftEnd.
Figure 10.8
Programming a custom
class’s event
Events usually pass information to the calling application. In VB, all events pass two arguments
to the application: a reference to the object that fired the event, and another argument (which is an
object and contains information specific to the event).
The arguments of an event are declared just like the arguments of a procedure. The following
statement declares an event that’s fired when the class completes the download of a file. The event
passes three parameter values to the application that intercepts it:
Public Event CompletedDownload(ByVal fileURL As String,
ByVal fileName As String, ByVal fileLength As Long)
The parameters passed to the application through this event are the URL from which the file
was downloaded, the path of a file where the downloaded information was stored, and the length
of the file. To raise this event from within a class’s code, call the RaiseEvent statement as before,
passing three values of the appropriate type, as shown next:
RaiseEvent CompletedDownload(”http://www.server.com/file.txt”,
”d:\temp\A90100.txt”, 144329)Petroutsos c10.tex V3 - 01/28/2008 1:32pm Page 380
380 CHAPTER 10 BUILDING CUSTOM CLASSES
When coding the event’s handler, you can access these arguments and use them as you wish.
Alternatively, you could create a new object, the DownloadedFileArgument type, and expose
these arguments as properties:
Public Class DownloadedFileArgument
Public FileName as String
Public TempLocation As String
Public FileSize As Long
End Class
Then you can declare the event’s signature by using the DownLoadedFileArgument type in the
argument list:
Public Event CompletedDownload(ByVal sender As Object,
ByVal e As DownloadedFileArgument)
To fire the CompletedDownload event from within your class’s code, create an instance of
the DownLoadedFileArgument class, set its properties and then call the RaiseEvent method, as
shown here:
Dim DArgument As New DownloadedFileArgument
DArgument.FileName = ”http://www.server.com/file.txt”
DArgument.TempLocation = ”d:\temp\A90100.txt”
DArgument.FileSize = 144329
RaiseEvent Fired(Me, DArgument)
To intercept this event in your test application, declare an object of the appropriate type with
the WithEvents keyword and write an event handler for the CompletedDownload event:
Public WithEvents obj As New EventFiringClass
Private Sub obj Fired(ByVal sender As Object,
ByVal e As Firing.DownloadedFileArgument)
Handles obj.CompletedDownload
MsgBox(”Event fired” & vbCrLf &
e.FileName & vbCrLf &
e.TempLocation & vbCrLf &
e.FileSize.ToString)
End Sub
That’s all it takes to fire an event from within your custom class. In Chapter 12, ‘‘Building
Custom Windows Controls,’’ you will find several examples of custom events.
Instance and Shared Methods
As you have seen in earlier chapters, some classes allow you to call some of their members without
first creating an instance of the class. The DateTime class, for example, exposes the IsLeapYear
method, which accepts as an argument a numeric value and returns a True/False value that indi-
cateswhether the year is a leap year. You can call thismethod through the DateTime (or Date) class
without having to create a variable of the DateTime type, as shown in the following statement:
If DateTime.IsLeapYear(1999) Then
{ process a leap year}
End IfPetroutsos c10.tex V3 - 01/28/2008 1:32pm Page 381
BUILDING THE MINIMAL CLASS 381
A typical example of classes that can be used without explicit instances is the Math class. To
calculate the logarithm of a number, you can use an expression such as this one:
Math.Log(3.333)
The properties and methods that don’t require you to create an instance of the class before you
call them are called shared methods. Methods that must be applied to an instance of the class are
called instance methods. By default, all methods are instance methods. To create a shared method,
you must prefix the corresponding function declaration with the Shared keyword, just like a
shared property.
Why do we need shared methods, and when should we create them? If a method doesn’t apply
to a specific instance of a class, make it shared. In other words, if a method doesn’t act on the
properties of the current instance of the class, it should be shared. Let’s consider the DateTime
class. The DaysInMonth method returns the number of days in the month (of a specific year) that
is passed to themethod as an argument. You don’t really need to create an instance of a Date object
to find out the number of days in a specific month of a specific year, so the DaysInMonth method
is a shared method and can be called as follows:
DateTime.DaysInMonth(2004, 2)
Think of the DaysInMonth method this way: Do I need to create a new date to find out if a
specific month has 30 or 31 days? If the answer is no, then the method is a candidate for a shared
implementation.
The AddDays method, on the other hand, is an instance method. We have a date to which we
want to add a number of days and construct a new date. In this case, it makes sense to apply the
method to an instance of the class — the instance that represents the date to which we add the
number of days.
If you spend amoment to reflect on shared and instancemembers, you’ll come to the conclusion
that all members could have been implemented as shared members and accept the data they
act upon as arguments. The idea behind classes, however, is to combine data with code. If you
implement a class with shared members, you lose one of the major advantages of OOP. Building
a class with shared members only is equivalent to a collection of functions, and the Math class of
the Framework is just that.
The SharedMembers sample project is a simple class that demonstrates the differences between
a shared and an instance method. Both methods do the same thing: They reverse the characters
in a string. The IReverseString method is an instance method; it reverses the current instance
of the class, which is a string. The SReverseString method is a shared method; it reverses its
argument. Listing 10.19 shows the code that implements the SharedMembersClass component.
Listing 10.19: A Class with a Shared and an InstanceMethod
Public Class SharedMembersClass
Private strProperty As String
Sub New(ByVal str As String)
strProperty = str
End Sub
Public Function IReverseString() As StringPetroutsos c10.tex V3 - 01/28/2008 1:32pm Page 382
382 CHAPTER 10 BUILDING CUSTOM CLASSES
Return (StrReverse(strProperty))
End Function
Public Shared Function SReverseString(ByVal str As String) As String
Return (StrReverse(str))
End Function
End Class
The instance method acts on the current instance of the class. This means that the class must
be initialized to a string, and this is why the New constructor requires a string argument. To test
the class, add a form to the project, make it the Startup object, and add two buttons to it. The code
behind the two buttons is shown next:
Private Sub Button1 Click(...) Handles Button1.Click
Dim testString As String = ”ABCDEFGHIJKLMNOPQRSTUVWXYZ”
Dim obj As New SharedMembersClass(testString)
Debug.WriteLine(obj.IReverseString)
End Sub
Private Sub Button2 Click(...) Handles Button2.Click
Dim testString As String = ”ABCDEFGHIJKLMNOPQRSTUVWXYZ”
Debug.WriteLine(SharedMembersClass.SReverseString(testString))
End Sub
The code behind the first button creates a new instance of the SharedMembersClass and calls
its IReverseString method. The second button calls the SReverseString method through the
class’s name and passes the string to be reversed as an argument to the method.
A class can also expose shared properties. There are situations in which you want all instances
of a class to see the same property value. Let’s say you want to keep track of the users currently
accessing your class. You can declare a method that must be called to enable the class, and this
method signals that another user has requested your class. This method could establish a connec-
tion to a database or open a file. We’ll call it the Connect method. Every time an application calls
the Connect method, you can increase an internal variable by one. Likewise, every time an appli-
cation calls the Disconnect method, the same internal variable is decreased by one. This internal
variable can’t be private because it will be initialized to zero with each new instance of the class.
You need a variable that is common to all instances of the class. Such a variable should be declared
with the Shared keyword.
Let’s add a shared variable to our Minimal class. We’ll call it LoggedUsers, and it will be
read-only. Its value is reported via the Users property, and only the Connect and Disconnect
methods can change its value. Listing 10.20 is the code you must add to the Minimal class to
implement a shared property.
Listing 10.20: Implementing a Shared Property
Shared LoggedUsers As Integer
ReadOnly Property Users() As Integer
Get
Users = LoggedUsersPetroutsos c10.tex V3 - 01/28/2008 1:32pm Page 383
BUILDING THE MINIMAL CLASS 383
End Get
End Property
Public Function Connect() As Integer
LoggedUsers = LoggedUsers + 1
{ your own code here}
End Function
Public Function Disconnect() As Integer
If LoggedUsers > 1 Then
LoggedUsers = LoggedUsers - 1
End If
{ your own code here}
End Function
To test the shared variable, add a new button to the form and enter the code in Listing 10.21
in its Click event handler. (The lines with the bold numbers are the values reported by the class;
they’re not part of the listing.)
Listing 10.21: Testing the LoggedUsers Shared Property
Protected Sub Button5 Click(ByVal sender As Object,
ByVal e As System.EventArgs)
Dim obj1 As New SharedMemberClass
obj1.Connect()
Debug.WriteLine(obj1.Users)
1
obj1.Connect()
Debug.WriteLine(obj1.Users)
2
Dim obj2 As New SharedMemberClass
obj2.Connect()
Debug.WriteLine(obj1.Users)
3
Debug.WriteLine(obj2.Users)
3
Obj2.Disconnect()
Debug.WriteLine(obj2.Users)
2
End Sub
If you run the application, you’ll see the values displayed under each Debug.WriteLine state-
ment in the Output window. As you can see, both the obj1 and obj2 variables access the same
value of the Users property. Shared variables are commonly used in classes that run on a server
and servicemultiple applications. In effect, they’re the class’s global variables, which can be shared
among all the instances of a class. You can use shared variables to keep track of the total number of
rows accessed by all users of the class in a database, connection time, and other similar quantities.Petroutsos c10.tex V3 - 01/28/2008 1:32pm Page 384
384 CHAPTER 10 BUILDING CUSTOM CLASSES
A‘‘Real’’ Class
This section covers a more-practical class that exposes three methods for manipulating strings.
I have used these methods in many projects, and I’m sure many readers will have good use for
them — at least one of them. The first two methods are the ExtractPathName and ExtractFile-
Name methods, which extract the filename and pathname from a full filename. If the full name of
a file is C:\Documents\Recipes\Chinese\Won Ton.txt,the ExtractPathName method will re-
turn the substring C:\Documents\Recipes\Chinese\,andthe ExtractFileName method will
return the substring Won Ton.txt.
You can use the Split method of the String class to extract all the parts of a delimited string.
Extracting the pathname and filename of a complete filename is so common in programming that
it’s a good idea to implement the corresponding functions as methods in a custom class. You can
also use the Path object, which exposes a similar functionality. (The Path object is discussed in
Chapter 15, ‘‘Accessing Folders and Files.’’)
The thirdmethod,which is called Num2String, converts numeric values (amounts) to the equiv-
alent strings. For example, it can convert the amount $12,544 to the string Twelve Thousand, Five
Hundred And Forty Four dollars. No other class in the Framework provides this functionality,
and any program that prints checks can use this class.
Parsing a Filename
Let’s start with the two methods that parse a complete filename. These methods are implemented
as Public functions, and they’re quite simple. Start a new project, rename the form to TestForm,
and add a Class to the project. Name the class and the project StringTools. Then enter the code of
Listing 10.22 in the Class module.
Listing 10.22: The ExtractFileName and ExtractPathNameMethods
Public Function ExtractFileName(ByVal PathFileName As String) As String
Dim delimiterPosition As Integer
delimiterPosition = PathFileName.LastIndexOf(”\”)
If delimiterPosition > 0 Then
Return PathFileName.Substring(delimiterPosition + 1)
Else
Return PathFileName
End If
End Function
Public Function ExtractPathName(ByVal PathFileName As String) As String
Dim delimiterPosition As Integer
delimiterPosition = PathFileName.LastIndexOf(”\”)
If delimiterPosition > 0 Then
Return PathFileName.Substring(0, delimiterPosition)
Else
Return ””
End If
End FunctionPetroutsos c10.tex V3 - 01/28/2008 1:32pm Page 385
A ‘‘REAL’’ CLASS 385
These are two simple functions that parse the string passed as an argument. If the string
contains no delimiter, it’s assumed that the entire argument is just a filename.
Converting Numbers to Strings
The Num2String method is far more complicated, but if you can implement it as a regular func-
tion, it doesn’t take any more effort to turn it into a method. The listing of Num2String is shown in
Listing 10.23. First, it formats the billions in the value (if the value is that large); then it formats
the millions, thousands, units, and finally the decimal part, which can contain no more than two
digits.
Listing 10.23: Converting Numbers to Strings
Public Function Num2String(ByVal number As Decimal) As String
Dim biln As Decimal, miln As Decimal,
thou As Decimal, hund As Decimal
Dim ten As Integer, units As Integer
Dim strNumber As String
If number > 999999999999.99 Then
Return (”***”)
Exit Function
End If
biln = Math.Floor(number / 1000000000)
If biln > 0 Then
strNumber = FormatNum(biln) & ” Billion” & Pad()
miln = Math.Floor((number - biln * 1000000000) / 1000000)
If miln > 0 Then
strNumber = strNumber & FormatNum(miln) & ” Million” & Pad()
thou = Math.Floor((number - biln * 1000000000 - miln * 1000000) / 1000)
If thou > 0 Then
strNumber = strNumber & FormatNum(thou) & ” Thousand” & Pad()
hund = Math.Floor(number - biln * 1000000000 - miln * 1000000 - thou * 1000)
If hund > 0 Then strNumber = strNumber & FormatNum(hund)
If Right(strNumber, 1) = ”,” Then
strNumber = Left(strNumber, Len(strNumber) - 1)
If Left(strNumber, 1) = ”,” Then
strNumber = Right(strNumber, Len(strNumber) - 1)
If number <> Math.Floor(number) Then
strNumber = strNumber &
FormatDecimal(CInt((number - Int(number)) * 100))
Else
strNumber = strNumber & ” dollars”
End If
Return (Delimit(SetCase(strNumber)))
End Function
Each group of three digits (million, thousand, and so on) is formatted by the FormatNum() func-
tion. Then the appropriate string is appended (Million, Thousand,andsoon).The FormatNum()
function, which converts a numeric value less than 1,000 to the equivalent string, is shown in
Listing 10.24.Petroutsos c10.tex V3 - 01/28/2008 1:32pm Page 386
386 CHAPTER 10 BUILDING CUSTOM CLASSES
Listing 10.24: The FormatNum() Function
Private Function FormatNum(ByVal num As Decimal) As String
Dim digit100 As Decimal, digit10 As Decimal, digit1 As Decimal
Dim strNum As String
digit100 = Math.Floor(num / 100)
If digit100 > 0 Then strNum = Format100(digit100)
digit10 = Math.Floor((num - digit100 * 100))
If digit10 > 0 Then
If strNum <> ”” Then
strNum = strNum & ” And ” & Format10(digit10)
Else
strNum = Format10(digit10)
End If
End If
Retutn (strNum)
End Function
The FormatNum() function formats a three-digit number as a string. To do so, it calls the For-
mat100() function to format the hundreds, and the Format10() function formats the tens. The
Format10() function calls the Format1() function to format the units. I will not show the code for
these functions; you can find it in the StringTools project. You’d probably use similar functions to
implement the Num2String method as a function. Instead, I will focus on a few peripheral issues,
such as the enumerations used by the class as property values.
To make the Num2String method more flexible, the class exposes the Case, Delimiter,and
Padding properties. The Case property determines the case of the characters in the string returned
by the method. The Delimiter property specifies the special characters that should appear before
and after the string. Finally, the Padding property specifies the character that will appear between
groups of digits. The values each of these properties can take on are members of the appropriate
enumeration:
PaddingEnum DelimiterEnum CaseEnum
paddingCommas delimiterNone caseCaps
paddingSpaces delimiterAsterisk caseLower
paddingDashes delimiter3Asterisks caseUpper
The values under each property name are implemented as enumerations, and you need not
memorize their names. As you enter the name of the property followed by the equal sign, the
appropriate list of values will pop up, and you can select the desired member. Listing 10.25
presents the UseCaseEnum enumeration and the implementation of the UseCase property.
Listing 10.25: The CaseEnum Enumeration and the UseCase Property
Enum CaseEnum
caseCapsPetroutsos c10.tex V3 - 01/28/2008 1:32pm Page 387
A ‘‘REAL’’ CLASS 387
caseLower
caseUpper
End Enum
Private varUseCase As CaseEnum
Public Property [Case]() As CaseEnum
Get
Return (varUseCase)
End Get
Set
varUseCase = Value
End Set
End Property
Notice that the name of the Case property is enclosed in square brackets. This is necessary
when you’re using a reserved keyword as a variable, property, method, or enumeration member
name. Alternatively, you can use a different name for the property to avoid the conflict altogether.
After the declaration of the enumeration and the Property procedure are in place, the coding of the
rest of the class is simplified a great deal. The Num2String() function, for example, calls the Pad()
method after each three-digit group. The separator is specified by the UseDelimiter property,
whosetypeis clsPadding.The Pad() function uses the members of the UsePaddingEnum enumer-
ation to make the code easier to read. As soon as you enter the Case keyword, the list of values
that can be used in the Select Case statement will appear automatically, and you can select the
desired member. Here’s the code of the Pad() function:
Private Function Pad() As String
Select Case varUsePadding
Case PaddingEnum.paddingSpaces : Return (””)
Case PaddingEnum.paddingDashes : Return (”-”)
Case PaddingEnum.paddingCommas : Return (”,”)
End Select
End Function
To test the StringTools class, create a test form like the one shown in Figure 10.9. Then enter the
code from Listing 10.26 in the Click event handler of the two buttons.
Listing 10.26: Testing the StringTools Class
Protected Sub Button1 Click(...) Handles Button1.Click
TextBox1.Text = Convert.ToDecimal(
TextBox1.Text).ToString(”#,###.00”)
Dim objStrTools As New StringTools()
objStrTools.Case = StringTools.CaseEnum.CaseCaps
objStrTools.Delimiter = StringTools.DelimitEnum.DelimiterNone
objStrTools.Padding = StringTools.PaddingEnum.PaddingCommas
TextBox2.Text = objStrTools.Num2String(Convert.ToDecimal(TextBox1.Text))
End Sub
Protected Sub Button2 Click(...) Handles Button2.ClickPetroutsos c10.tex V3 - 01/28/2008 1:32pm Page 388
388 CHAPTER 10 BUILDING CUSTOM CLASSES
Dim objStrTools As New StringTools()
openFileDialog1.ShowDialog()
Dim fName as String
fName = OpenFileDialog1.FileName
Debug.writeline(objStrTools.ExtractPathName(fName))
Debug.WriteLine(objStrTools.ExtractFileName(fName))
End Sub
Figure 10.9
The test form of the
StringTools class
OperatorOverloading
In this section you’ll learn about an interesting (but quite optional) feature of class design: how to
customize the usual operators. Some operators in Visual Basic act differently on various types of
data. The addition operator ( + ) is the most typical example. When used with numbers, the addi-
tion operator adds them. When used with strings, however, it concatenates the strings. The same
operator can perform even more complicated calculations with the more-elaborate data types.
When you add two variables of the TimeSpan type, the addition operator adds their durations
and returns a new TimeSpan object. If you execute the following statements, the value 3882 will
be printed in the Output window (the number of seconds in a time span of 1 hour, 4 minutes, and
42 seconds):
Dim TS1 As New TimeSpan(1, 0, 30)
Dim TS2 As New TimeSpan(0, 4, 12)
Debug.WriteLine((TS1 + TS2).TotalSeconds.ToString)
The TimeSpan class is discussed in detail in Chapter 13, ‘‘Handling Strings, Characters, and
Dates,’’ but for the purposes of the preceding example, all you need to know is that variable TS1
represents a time span of 1 hour and 30 seconds, while TS2 represents a time span of 4 minutes
and 12 seconds. Their sum is a new time span of 1 hour, 4 minutes and 42 seconds. So far you saw
how to overload methods and how the overloaded forms of a method can simplify development.
Sometimes it makes sense to alter the default function of an operator. Let’s say you designed a
class for representing lengths in meters and centimeters, something like the following:
Dim MU As New MetricUnits
MU.Meters = 1
MU.Centimeters = 78Petroutsos c10.tex V3 - 01/28/2008 1:32pm Page 389
OPERATOR OVERLOADING 389
The MetricUnits class allows you to specify lengths as an integer number of meters and cen-
timeters (presumably, you don’t need any more accuracy). The most common operation you’ll
perform with this class is to add and subtract lengths. However, you can’t directly add two objects
of the MetricUnits type by using a statement such as this:
TotalLength = MU1 + MU2
Wouldn’t it be nice if you could add two custom objects by using the addition operator? For
this to happen, you should be able to overload the addition operator, just as you can overload a
method. Indeed, it’s possible to overload an operator for your custom classes and write statements
like the preceding one. Let’s design a class to express lengths in metric and English units, and then
overload the basic operators for this class.
To overload an operator, you must create an Operator procedure, which is basically a function
with an odd name: the name of the operator you want to overload. The Operator procedure
accepts as arguments two values of the custom type (the type for which you’re overloading the
operator) and returns a value of the same type. Here’s the outline of an Operator procedure that
overloads the addition operator:
Public Shared Operator + (
ByVal length1 As MetricUnits,
ByVal length2 As MetricUnits) As MetricUnits
End Operator
The procedure’s body contains the statements that add the two arguments as units of length,
not as numeric values. Overloading operators is a straightforward process that can help you create
elegant classes that can be manipulated with the common operators.
VB 2008 at Work: The LengthUnits Class
To demonstrate the overloading of common operators, I included the LengthUnits project, which
is a simple class for representing distances in English and metric units. Listing 10.27 shows the
definition of the MetricUnits class, which represents lengths in meters and centimeters.
Listing 10.27: TheMetricUnits Class
Public Class MetricUnits
Private Meters As Integer
Private Centimeters As Integer
Public Sub New()
End Sub
Public Sub New(ByVal meters As Integer, ByVal centimeters As Integer)
Me.Meters = meters
Me.Centimeters = centimeters
End Sub
Public Property Meters() As IntegerPetroutsos c10.tex V3 - 01/28/2008 1:32pm Page 390
390 CHAPTER 10 BUILDING CUSTOM CLASSES
Get
Return Meters
End Get
Set(ByVal Value As Integer)
Meters = Value
End Set
End Property
Public Property Centimeters() As Integer
Get
Return Centimeters
End Get
Set(ByVal Value As Integer)
If value > 100 Then
Meters += Convert.ToInt32(Math.Floor(Value / 100))
Centimeters = (Value Mod 100)
Else
Centimeters = value
End If
End Set
End Property
Public Overloads Function Tostring() As String
Dim str As String = Math.Abs( Meters).ToString & ” meters, ” &
Math.Abs( Centimeters).ToString & ” centimeters”
If Meters < 0Or( Meters = 0 And Centimeters < 0) Then
str = ”-” & str
End If
Return str
End Function
End Class
The class uses the private variables Meters and Centimeters to store the two values that
determine the length of the current instance of the class. These variables are exposed as the Meters
and Centimeters properties. Notice the two forms of the constructor and the custom ToString
method. Because the calling application may supply a value that exceeds 100 for the Centime-
ters property, the code that implements the Centimeters property checks for this condition
and increases the Meters property, if needed. It allows the calling application to set the Cen-
timeters property to 252, but internally it increases the Meters local variable by 2 and sets
the Centimenters local variable to 52. The ToString method returns the value of the current
instance of the class as a string such as 1.98, but it inserts a minus sign in front of it if it’s negative.
If you open the sample project, you’ll find the implementation of the EnglishUnits class, which
represents lengths in feet and inches. The code is quite similar.
There’s nothing out of the ordinary so far; it’s actually a trivial class.We can turn it into a highly
usable class by overloading the basic operators for the MetricUnits class: namely the addition and
subtraction operators. Add the Operator procedures shown in Listing 10.28 to the class’s code.Petroutsos c10.tex V3 - 01/28/2008 1:32pm Page 391
OPERATOR OVERLOADING 391
Listing 10.28: Overloading Operators for theMetricUnits Class
Public Shared Operator + (
ByVal length1 As MetricUnits,
ByVal length2 As MetricUnits) As MetricUnits
Dim result As New metricUnits
result.Meters = 0
result.Centimeters =
length1.Meters * 100 + length1.Centimeters +
length2.Meters * 100 + length2.Centimeters
Return result
End Operator
Public Shared Operator - (
ByVal length1 As MetricUnits,
ByVal length2 As MetricUnits) As MetricUnits
Dim result As New MetricUnits
result.Meters = 0
result.Centimeters =
length1.Meters * 100 + length1.Centimeters -
length2.Meters * 100 - length2.Centimeters
Return result
End Operator
These two procedures turned an ordinary class into an elegant custom data type. You can now
create MetricUnits variables in your code and manipulate them with the addition and subtraction
operators as if they were simple numeric data types. The following code segment exercises the
MetricUnits class:
Dim MU1 As New MetricUnits
MU1.Centimeters = 194
Debug.WriteLine(”194 centimeters is ” & MU1.Tostring & ” meters”)
194 centimeters is 1.94 meters
Dim MU2 As New MetricUnits
MU2.Meters = 1
MU2.Centimeters = 189
Debug.WriteLine(”1 meter and 189 centimeters is ” & MU2.Tostring & ” meters”)
1 meter and 189 centimeters is 2.89 meters
Debug.WriteLine(”194 + 289 centimeters is ” & (MU1 + MU2).Tostring & ” meters”)
194 + 289 centimeters is 4.83 meters
Debug.WriteLine(”194 - 289 centimeters is ” & (MU1 - MU2).Tostring & ” meters”)
The negative of 1.94 is -1.94
MU1.Meters = 4
MU1.Centimeters = 63
Dim EU1 As EnglishUnits = CType(MU1, EnglishUnits)
Debug.WriteLine(”4.62 meters are ” & EU1.Tostring)
4.62 meters are 15’ 2”
MU1 = CType(EU1, MetricUnits)Petroutsos c10.tex V3 - 01/28/2008 1:32pm Page 392
392 CHAPTER 10 BUILDING CUSTOM CLASSES
Debug.WriteLine(EU1.Tostring & ” are ” & MU1.Tostring & ” meters”)
15’ 2” are 4.62 meters
If you execute the preceding statements, the highlighted values will appear in the Output win-
dow. (The LengthUnits sample project uses a TextBox control to display its output.) Figure 10.10
shows the test project for theMetricUnits and EnglishUnits classes. The last fewstatements convert
values between metric and English units, and you’ll see the implementation of these operations
momentarily.
Figure 10.10
Exercising the members
of the MetricUnits class
Implementing Unary Operators
In addition to being the subtraction operator, the minus symbol is also a unary operator (it negates
the following value). If you attempt to negate a MetricUnits variable, an error will be generated
because the subtraction operator expects two values — one on either side of it. In addition to the
subtraction operator (which is a binary operator because it operates on two values), we must
define the negation operator (which is a unary operator because it operates on a single value). The
unary minus operator negates the following value, so a new definition of the subtraction Operator
procedure is needed. This definition will overload the existing one, as follows:
Public Overloads Shared Operator -(
ByVal length1 As MetricUnits) As MetricUnits
Dim result As New MetricUnits
result.Meters = -length1.Meters
result.Centimeters = -length1.Centimeters
Return result
End OperatorPetroutsos c10.tex V3 - 01/28/2008 1:32pm Page 393
OPERATOR OVERLOADING 393
To negate a length unit stored in a variable of the MetricUnits type, use statements such as the
following:
MU2 = -MU1
Debug.Write(MU2.Tostring)
Debug.Write((-MU1).Tostring)
Both statements will print the following in the Output window:
-1 meters, -94 centimeters
There are several unary operators, which you can overload in your custom classes as needed.
There’s the unary + operator (not a common operator), and the Not, IsTrue,and IsFalse oper-
ators, which are logical operators. The last unary operator is the CType operator, which is exposed
as a method of the custom class and is explained next.
Handling Variants
To make your custom data type play well with the other data types, you must also provide a
CType() function that can convert a value of the MetricUnits type to any other type. It doesn’t
make much sense to convert MetricUnits to dates or any of the built-in objects, but let’s say you
have another class: the EnglishUnits class. This class is similar to the MetricUnits class, but it
exposes the Inches and Feet properties in place of the Meters and Centimeters properties. The
CType() function of the MetricUnits class, which will convert MetricUnits to EnglishUnits, is
shown next:
Public Overloads Shared Widening Operator
CType(ByVal MU As MetricUnits) As EnglishUnits
Dim EU As New EnglishUnits
EU.Inches = Convert.ToInt32(
(MU.Meters * 100 + MU.Centimeters) / 2.54)
Return EU
End Operator
Do you remember the implicit narrowing and widening conversions we discussed in
Chapter 2? An attempt to assign an integer value to a decimal variable will produce a warning,
but the statement will be executed because it’s a widening conversion (no loss of accuracy will
occur). The opposite is not true. If the Strict option is on, the compiler won’t allow narrowing con-
versions because not all Decimal values can be mapped to Integers. To help the compiler enforce
strict types, you can use the appropriate keyword to specify whether the CType() function per-
forms a widening or a narrowing conversion. The CType() procedure is shared and overloads the
default implementation, which explains all the keywords prefixing its declaration. The following
statements exercise the CType method of the MetricUnits class:
Debug.Write(MU1.Tostring)
1 meters, 94 centimeters
Debug.WriteLine(CType(MU1, EnglishUnits).Tostring)
6 feet, 4 inchesPetroutsos c10.tex V3 - 01/28/2008 1:32pm Page 394
394 CHAPTER 10 BUILDING CUSTOM CLASSES
The output of the two statements is highlighted. Both classes expose integer properties, so the
Widening or Narrowing keyword isn’t really important. In other situations, you must carefully
specify the type of the conversion to help the compiler generate the appropriate warnings (or
exceptions, if needed).
The CType operatorwe added to theMetricUnits class can only convert values of theMetricUnit
type to values of the EnglishUnit type. If it makes sense to convert MetricUnits variables to other
types, you must provide more overloaded forms of the CType() procedure. For example, you
can convert them to numeric values (the numeric value could be the length in centimeters or a
double value that represents the same length in meters). The compiler sees the return type(s) of
the various overloaded forms of the CType operator, knows whether the requested conversion is
possible, and generates the appropriate exception.
In short, operator overloading isn’t complicated, but it adds a touch of elegance to a custom
class and enables variables of this type to mix well with the other data types. If you like math,
you could implement classes to represent matrices, or complex numbers, and overload the usual
operators for addition, multiplication, and so on.
The BottomLine
Build your own classes. Classes contain code that executes without interacting with the
user. The class’s code is made up of three distinct segments: the declaration of the private
variables, the property procedures that set or read the values of the private variables, and the
methods, which are implemented as Public subroutines or functions. Only the Public entities
(properties and methods) are accessible by any code outside the class. Optionally, you can
implement events that are fired from within the class’s code. Classes are referenced through
variables of the appropriate type, and applications call the members of the class through these
variables. Every time a method is called, or a property is set or read, the corresponding code
in the class
is executed.
Master It How do you implement properties and methods in a custom class?
Master It How would you use a constructor to allow developers to create an instance of
your class and populate it with initial data?
Use custom classes in your projects. To use a custom class in your project, you must add
to the project a reference to the class you want to use. If the class belongs to the same project,
you don’t have to do anything. If the class belongs to another project, you must right-click the
project’s name in the Solution Explorer and select Add Reference from the shortcut menu. In
the Add Reference dialog box that appears, switch to the Browse tab and locate the DLL file
with the class’s implementation (it will be a DLL file in the project’s Bin folder). Select the name
of this file and click OK to add the reference and close the dialog box.
Master It How will you call the two constructors of the preceding Master It sections in an
application that uses the custom class to represent books?
Customize the usual operators for your classes. Overloading is a common theme in coding
classes (or plain procedures) with Visual Basic. In addition to overloading methods, you can
overload operators. In other words, you can define the rules for adding or subtracting two cus-
tom objects, if this makes sense for your application.
Master It When should you overload operators in a custom class, and why?Petroutsos c11.tex V2 - 01/28/2008 1:36pm Page 395
Chapter 11
Working with Objects
This chapter continues the discussion of object-oriented programming (OOP) and covers some of
its more-advanced, but truly useful, concepts: inheritance and polymorphism. Instead of jumping
to the topic of inheritance, I’ll start with a quick overview of what you learned in the previous
chapter and how to apply this knowledge.
Inheritance is discussed later in this chapter, along with polymorphism, another powerful OOP
technique, and interfaces. But first make sure that you understand the basics of OOP because
things aren’t always as simple as they look (but are quite often simpler than you think).
In this chapter, you’ll learn how to:
◆ Extend existing classes using inheritance
◆ Develop flexible classes using polymorphism
Issues inObject-Oriented Programming
Building classes and using themin your code is fairly simple, but there are a few points about OOP
that can cause confusion. To help you make the most of OOP and get up to speed, I’m including
a list of related topics that are known to cause confusion to programmers— and not only begin-
ners. If you understand the topics of the following paragraphs and how they relate to the topics
discussed in the previous chapter, you’re more than familiar with the principles of OOP and you
can apply them to your projects immediately.
Classes versus Objects
Classes are templates that we use to create new objects. In effect, they’re the blueprints used to
manufacture objects in our code. Another way to think of classes is as custom types. After you add
the class Customer to your project (or a reference to the DLL that implements the Customer class),
you can declare variables of the Customer type, just as you declare integers and strings. The code
of the class is loaded into the memory, and a new set of local variables is created. This process is
referred to as class instantiation: Creating an object of a custom type is the same as instantiating the
class that implements the custom type. For each object of the Customer type, there’s a set of local
variables, as they’re declared in the class’s code. The various procedures of the class are invoked
as needed by the Common Language Runtime (CLR) and they act on the set of local variables that
correspond to the current instance of the class. Some of the local variables may be common among
all instances of a class: These are the variables that correspond to shared properties (properties
that are being shared by all instances of a class).
When you create a new variable of the Customer type, the New() procedure of the Customer
class is invoked. The New() procedure is known as the class’s constructor.EachclasshasadefaultPetroutsos c11.tex V2 - 01/28/2008 1:36pm Page 396
396 CHAPTER 11 WORKING WITH OBJECTS
constructor that accepts no arguments, even if the class doesn’t contain a New() subroutine. This
default constructor is invoked every time a statement similar to the following is executed:
Dim cust As New Customer
You can overload the New() procedure by specifying arguments, and you should try to provide
one or more parameterized constructors. Parameterized constructors allow you (or any developer
using your class) to create meaningful instances of the class. Sure, you can create a new Customer
object with no data in it, but a Customer object with a name and company makes more sense. The
parameterized constructor initializes some of the most characteristic properties of the object.
Objects versus Object Variables
All variables that refer to objects are called object variables. (The other type of variables are value
variables, which store base data types, such as characters, integers, strings, and dates.) In declaring
object variables, we usually use the New keyword, which is the only way to create a new object. If
you omit this keyword from a declaration, only a variable of the Customer type will be created,
but no instance of the Customer class will be created in memory, and the variable won’t point to
an actual object. The following statement declares a variable of the Customer type, but doesn’t
create an object:
Dim Cust As Customer
If you attempt to access a member of the Customer class through the Cust variable, the infa-
mous NullReferenceException will be thrown. The description of this exception is Object
reference not set to an instance of an object, which means that the Cust variable doesn’t point to
an instance of the Customer class. Actually, the editor will catch this error and will underline the
name of the variable. If you hover the mouse pointer over the name of the variable in question, the
following explanation will appear on a ToolTip box: Variable Cust is used before it has been assigned
a value. A Null Reference exception could result at runtime. Why bother declaring variables that don’t
point to specific objects? The Cust variable can be set later in the code to reference an existing
instance of the class:
Dim Cust As Customer
Dim Cust2 As New Customer
Cust = Cust2
After the execution of the preceding statements, both variables point to the same object in
memory, and you can set the properties of this object through either variable. You have two object
variables, but only one object in memory because only one of them was declared with the New
keyword. To set the Company property, you can use either one of the following statements, because
they both point to the same object in memory:
Cust.CompanyName = ”New Company Name”
or
Cust2.CompanyName = ”New Company Name”Petroutsos c11.tex V2 - 01/28/2008 1:36pm Page 397
ISSUES IN OBJECT-ORIENTED PROGRAMMING 397
The Cust variable is similar to a shortcut. When you create a shortcut to a specific file on your
desktop, you’re creating a reference to the original file. You do not create a new file or a copy of
the original file. You can use the shortcut to access the original file, just as we can use the Cust
variable to manipulate the properties of the Cust2 object in the preceding code sample.
It’s also common to declare object variables without the New keyword when we know we’re
going to use them la