- •Using Your Sybex Electronic Book
- •Acknowledgments
- •Contents at a Glance
- •Introduction
- •Who Should Read This Book?
- •How About the Advanced Topics?
- •The Structure of the Book
- •How to Reach the Author
- •The Integrated Development Environment
- •The Start Page
- •Project Types
- •Your First VB Application
- •Making the Application More Robust
- •Making the Application More User-Friendly
- •The IDE Components
- •The IDE Menu
- •The Toolbox Window
- •The Solution Explorer
- •The Properties Window
- •The Output Window
- •The Command Window
- •The Task List Window
- •Environment Options
- •A Few Common Properties
- •A Few Common Events
- •A Few Common Methods
- •Building a Console Application
- •Summary
- •Building a Loan Calculator
- •How the Loan Application Works
- •Designing the User Interface
- •Programming the Loan Application
- •Validating the Data
- •Building a Math Calculator
- •Designing the User Interface
- •Programming the MathCalculator App
- •Adding More Features
- •Exception Handling
- •Taking the LoanCalculator to the Web
- •Working with Multiple Forms
- •Working with Multiple Projects
- •Executable Files
- •Distributing an Application
- •VB.NET at Work: Creating a Windows Installer
- •Finishing the Windows Installer
- •Running the Windows Installer
- •Verifying the Installation
- •Summary
- •Variables
- •Declaring Variables
- •Types of Variables
- •Converting Variable Types
- •User-Defined Data Types
- •Examining Variable Types
- •Why Declare Variables?
- •A Variable’s Scope
- •The Lifetime of a Variable
- •Constants
- •Arrays
- •Declaring Arrays
- •Initializing Arrays
- •Array Limits
- •Multidimensional Arrays
- •Dynamic Arrays
- •Arrays of Arrays
- •Variables as Objects
- •So, What’s an Object?
- •Formatting Numbers
- •Formatting Dates
- •Flow-Control Statements
- •Test Structures
- •Loop Structures
- •Nested Control Structures
- •The Exit Statement
- •Summary
- •Modular Coding
- •Subroutines
- •Functions
- •Arguments
- •Argument-Passing Mechanisms
- •Event-Handler Arguments
- •Passing an Unknown Number of Arguments
- •Named Arguments
- •More Types of Function Return Values
- •Overloading Functions
- •Summary
- •The Appearance of Forms
- •Properties of the Form Control
- •Placing Controls on Forms
- •Setting the TabOrder
- •VB.NET at Work: The Contacts Project
- •Anchoring and Docking
- •Loading and Showing Forms
- •The Startup Form
- •Controlling One Form from within Another
- •Forms vs. Dialog Boxes
- •VB.NET at Work: The MultipleForms Project
- •Designing Menus
- •The Menu Editor
- •Manipulating Menus at Runtime
- •Building Dynamic Forms at Runtime
- •The Form.Controls Collection
- •VB.NET at Work: The DynamicForm Project
- •Creating Event Handlers at Runtime
- •Summary
- •The TextBox Control
- •Basic Properties
- •Text-Manipulation Properties
- •Text-Selection Properties
- •Text-Selection Methods
- •Undoing Edits
- •VB.NET at Work: The TextPad Project
- •Capturing Keystrokes
- •The ListBox, CheckedListBox, and ComboBox Controls
- •Basic Properties
- •The Items Collection
- •VB.NET at Work: The ListDemo Project
- •Searching
- •The ComboBox Control
- •The ScrollBar and TrackBar Controls
- •The ScrollBar Control
- •The TrackBar Control
- •Summary
- •The Common Dialog Controls
- •Using the Common Dialog Controls
- •The Color Dialog Box
- •The Font Dialog Box
- •The Open and Save As Dialog Boxes
- •The Print Dialog Box
- •The RichTextBox Control
- •The RTF Language
- •Methods
- •Advanced Editing Features
- •Cutting and Pasting
- •Searching in a RichTextBox Control
- •Formatting URLs
- •VB.NET at Work: The RTFPad Project
- •Summary
- •What Is a Class?
- •Building the Minimal Class
- •Adding Code to the Minimal Class
- •Property Procedures
- •Customizing Default Members
- •Custom Enumerations
- •Using the SimpleClass in Other Projects
- •Firing Events
- •Shared Properties
- •Parsing a Filename String
- •Reusing the StringTools Class
- •Encapsulation and Abstraction
- •Inheritance
- •Inheriting Existing Classes
- •Polymorphism
- •The Shape Class
- •Object Constructors and Destructors
- •Instance and Shared Methods
- •Who Can Inherit What?
- •Parent Class Keywords
- •Derived Class Keyword
- •Parent Class Member Keywords
- •Derived Class Member Keyword
- •MyBase and MyClass
- •Summary
- •On Designing Windows Controls
- •Enhancing Existing Controls
- •Building the FocusedTextBox Control
- •Building Compound Controls
- •VB.NET at Work: The ColorEdit Control
- •VB.NET at Work: The Label3D Control
- •Raising Events
- •Using the Custom Control in Other Projects
- •VB.NET at Work: The Alarm Control
- •Designing Irregularly Shaped Controls
- •Designing Owner-Drawn Menus
- •Designing Owner-Drawn ListBox Controls
- •Using ActiveX Controls
- •Summary
- •Programming Word
- •Objects That Represent Text
- •The Documents Collection and the Document Object
- •Spell-Checking Documents
- •Programming Excel
- •The Worksheets Collection and the Worksheet Object
- •The Range Object
- •Using Excel as a Math Parser
- •Programming Outlook
- •Retrieving Information
- •Recursive Scanning of the Contacts Folder
- •Summary
- •Advanced Array Topics
- •Sorting Arrays
- •Searching Arrays
- •Other Array Operations
- •Array Limitations
- •The ArrayList Collection
- •Creating an ArrayList
- •Adding and Removing Items
- •The HashTable Collection
- •VB.NET at Work: The WordFrequencies Project
- •The SortedList Class
- •The IEnumerator and IComparer Interfaces
- •Enumerating Collections
- •Custom Sorting
- •Custom Sorting of a SortedList
- •The Serialization Class
- •Serializing Individual Objects
- •Serializing a Collection
- •Deserializing Objects
- •Summary
- •Handling Strings and Characters
- •The Char Class
- •The String Class
- •The StringBuilder Class
- •VB.NET at Work: The StringReversal Project
- •VB.NET at Work: The CountWords Project
- •Handling Dates
- •The DateTime Class
- •The TimeSpan Class
- •VB.NET at Work: Timing Operations
- •Summary
- •Accessing Folders and Files
- •The Directory Class
- •The File Class
- •The DirectoryInfo Class
- •The FileInfo Class
- •The Path Class
- •VB.NET at Work: The CustomExplorer Project
- •Accessing Files
- •The FileStream Object
- •The StreamWriter Object
- •The StreamReader Object
- •Sending Data to a File
- •The BinaryWriter Object
- •The BinaryReader Object
- •VB.NET at Work: The RecordSave Project
- •The FileSystemWatcher Component
- •Properties
- •Events
- •VB.NET at Work: The FileSystemWatcher Project
- •Summary
- •Displaying Images
- •The Image Object
- •Exchanging Images through the Clipboard
- •Drawing with GDI+
- •The Basic Drawing Objects
- •Drawing Shapes
- •Drawing Methods
- •Gradients
- •Coordinate Transformations
- •Specifying Transformations
- •VB.NET at Work: Plotting Functions
- •Bitmaps
- •Specifying Colors
- •Defining Colors
- •Processing Bitmaps
- •Summary
- •The Printing Objects
- •PrintDocument
- •PrintDialog
- •PageSetupDialog
- •PrintPreviewDialog
- •PrintPreviewControl
- •Printer and Page Properties
- •Page Geometry
- •Printing Examples
- •Printing Tabular Data
- •Printing Plain Text
- •Printing Bitmaps
- •Using the PrintPreviewControl
- •Summary
- •Examining the Advanced Controls
- •How Tree Structures Work
- •The ImageList Control
- •The TreeView Control
- •Adding New Items at Design Time
- •Adding New Items at Runtime
- •Assigning Images to Nodes
- •Scanning the TreeView Control
- •The ListView Control
- •The Columns Collection
- •The ListItem Object
- •The Items Collection
- •The SubItems Collection
- •Summary
- •Types of Errors
- •Design-Time Errors
- •Runtime Errors
- •Logic Errors
- •Exceptions and Structured Exception Handling
- •Studying an Exception
- •Getting a Handle on this Exception
- •Finally (!)
- •Customizing Exception Handling
- •Throwing Your Own Exceptions
- •Debugging
- •Breakpoints
- •Stepping Through
- •The Local and Watch Windows
- •Summary
- •Basic Concepts
- •Recursion in Real Life
- •A Simple Example
- •Recursion by Mistake
- •Scanning Folders Recursively
- •Describing a Recursive Procedure
- •Translating the Description to Code
- •The Stack Mechanism
- •Stack Defined
- •Recursive Programming and the Stack
- •Passing Arguments through the Stack
- •Special Issues in Recursive Programming
- •Knowing When to Use Recursive Programming
- •Summary
- •MDI Applications: The Basics
- •Building an MDI Application
- •Built-In Capabilities of MDI Applications
- •Accessing Child Forms
- •Ending an MDI Application
- •A Scrollable PictureBox
- •Summary
- •What Is a Database?
- •Relational Databases
- •Exploring the Northwind Database
- •Exploring the Pubs Database
- •Understanding Relations
- •The Server Explorer
- •Working with Tables
- •Relationships, Indices, and Constraints
- •Structured Query Language
- •Executing SQL Statements
- •Selection Queries
- •Calculated Fields
- •SQL Joins
- •Action Queries
- •The Query Builder
- •The Query Builder Interface
- •SQL at Work: Calculating Sums
- •SQL at Work: Counting Rows
- •Limiting the Selection
- •Parameterized Queries
- •Calculated Columns
- •Specifying Left, Right, and Inner Joins
- •Stored Procedures
- •Summary
- •How About XML?
- •Creating a DataSet
- •The DataGrid Control
- •Data Binding
- •VB.NET at Work: The ViewEditCustomers Project
- •Binding Complex Controls
- •Programming the DataAdapter Object
- •The Command Objects
- •The Command and DataReader Objects
- •VB.NET at Work: The DataReader Project
- •VB.NET at Work: The StoredProcedure Project
- •Summary
- •The Structure of a DataSet
- •Navigating the Tables of a DataSet
- •Updating DataSets
- •The DataForm Wizard
- •Handling Identity Fields
- •Transactions
- •Performing Update Operations
- •Updating Tables Manually
- •Building and Using Custom DataSets
- •Summary
- •An HTML Primer
- •HTML Code Elements
- •Server-Client Interaction
- •The Structure of HTML Documents
- •URLs and Hyperlinks
- •The Basic HTML Tags
- •Inserting Graphics
- •Tables
- •Forms and Controls
- •Processing Requests on the Server
- •Building a Web Application
- •Interacting with a Web Application
- •Maintaining State
- •The Web Controls
- •The ASP.NET Objects
- •The Page Object
- •The Response Object
- •The Request Object
- •The Server Object
- •Using Cookies
- •Handling Multiple Forms in Web Applications
- •Summary
- •The Data-Bound Web Controls
- •Simple Data Binding
- •Binding to DataSets
- •Is It a Grid, or a Table?
- •Getting Orders on the Web
- •The Forms of the ProductSearch Application
- •Paging Large DataSets
- •Customizing the Appearance of the DataGrid Control
- •Programming the Select Button
- •Summary
- •How to Serve the Web
- •Building a Web Service
- •Consuming the Web Service
- •Maintaining State in Web Services
- •A Data-Driven Web Service
- •Consuming the Products Web Service in VB
- •Summary
BUILDING THE MINIMAL CLASS 335
Every time you create a new variable of the Minimal 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. You must still initialize the obj variable with the New keyword on a separate line:
obj = New Minimal()
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 Null—it hasn’t been initialized yet.
Property Procedures
The property1 and property2 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, like ages or zip codes? We should be able to invoke some code to validate the values assigned to the property. To do so, you must implement the properties with the so-called Property procedures.
Properties are implemented with a special type of procedure that contains a Get and Set section. The Set section of the procedure is invoked when the application attempts to set the property’s value, and the Get section is invoked when the application requests the property’s value. Usually, the value passed to the property is validated in the Set section and, if valid, 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 8.3 shows what the implementation of an Age property would look like.
Listing 8.3: Implementing Properties with Property Procedures
Private tAge As Integer
Property Age() As Integer
Get
Age = tAge
End Get
Set (ByVal Value As Integer)
If Value < 0 Or Value >= 125 Then
MsgBox(“Age must be positive and less than 125”)
Else
tAge = Value
End If
End Set
End Property
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |
336 Chapter 8 BUILDING CUSTOM CLASSES
tAge is the local variable where the age is stored. When a line like the following is executed in the application that uses your class, the Set section of the Property procedure is invoked:
obj.Age = 39
Since the value is valid, it’s stored in the tAge local variable. Likewise, when a line like the following one is executed,
Console.WriteLine(obj.Age)
the Get section of the Property procedure is invoked, and the value 39 is returned to the application. The Value keyword in the Set section represents the actual value that the calling code is attempting to assign to the property. You don’t declare this variable, and its name is always Value. The tAge variable is declared as private, because we don’t want any code outside the class to access it; this variable is used by the class to store the value of the Age property and can’t be manipulated directly. The
Age property is, of course, public, so that other applications can set it.
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
Console.WriteLine(“after setting the age to 39, age is “ & obj.Age.ToString) obj.Age = 199
Console.WriteLine(“after setting the age to 199, age is “ & obj.Age.ToString)
The value 39 will appear in the Output window. This means that the class accepted 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 125
then the value 39 will appear again in the Output window again. The attempt to set the age to 199 failed, so the property retains its previous value.
Raising Exceptions
Our error-trapping code works fine, but what good is it? Any developer using our class won’t be able to handle this error. You don’t want to display messages from within your class, because messages are intended for the final user. As a developer, 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 8.4) throws an argument exception if you attempt to assign an invalid value to it.
Listing 8.4: Throwing an Exception from within a Property Procedure
Private tAge As Integer
Property Age() As Integer
Get
Age = tAge
End Get
Set (ByVal Value As Integer)
If Value < 0 Or Value >= 125 Then
Dim AgeException As New ArgumentException()
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |
BUILDING THE MINIMAL CLASS 337
Throw AgeException
Else
tAge = Value
End If
End Set
End Property
You can test this property in your application; switch to the test form, and enter the statements of Listing 8.5 in a new button’s Click event handler (this is the code behind the Handle Exceptions button on the test form).
Listing 8.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 intercepted by the calling application, and developers using your class can write a robust application 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. 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 8.3). Our example continues with a default age of 30. But 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 application crash (but this isn’t your problem).
Figure 8.3
Raising an exception in the class’s code
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |
338 Chapter 8 BUILDING CUSTOM CLASSES
Implementing Read-Only Properties
Let’s make our class a little more complicated. Age is not usually requested on official documents. 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 8.6. Then enter the Property procedure from Listing 8.7 for the BDate property.
Listing 8.6: Making a Read-Only Property
Private tAge As Integer
ReadOnly Property Age() As Integer
Get
Age = tAge
End Get
End Property
Listing 8.7: The BDate Property
Private tBDate As Date
Property BDate() As Date Get
BDate = tBDate End Get
Set
If Not IsDate(Value) Then MsgBox(“Invalid date”) Exit Property
End If
If Value > Now() Or DateDiff(DateInterval.Year, Now(), Value) >= 125 Then MsgBox(“Age must be positive and less than 125”)
Else
tBDate = Value End If
End Set
End Property
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 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 125 years (just in case), it rejects the value. Otherwise it sets the value of the tBDate local variable.
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |
BUILDING THE MINIMAL CLASS 339
Now we must do something about the Age property’s value. The implementation of the Age property shown in Listing 8.6 allows you to read the value of the property, but not set it. However, you must update the tAge variable. Instead of maintaining a local variable for the age, we can calculate it every time the user requests the value of the Age property. Revise the Age property’s code to match Listing 8.8, so that it calculates the difference between the date of birth and the current date and returns the person’s age.
Listing 8.8: A Calculated Property
ReadOnly Property Age() As Integer
Get
Age = CInt(DateDiff(DateInterval.Year, Now(), tBDate))
End Get
End Property
Notice also that you no longer need the tAge local variable, because the age is calculated on-the-fly when requested. 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 may still return a negative value, if the user has changed the system’s date, but this is rather far-fetched.
You can implement write-only properties with the WriteOnly keyword. This type of property is implemented with a Set section only. But WriteOnly properties are of questionable value, and you’ll probably never use them.
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 methods to calculate insurance rates and anything you can think of. Add a few members to the class, and check them out.
Before ending this section, let’s experiment a little with object variables. We’ll create two variables of the Minimal class and set some properties. Then, we’ll call the Equals method to compare them. Enter the lines of Listing 8.9 in a new button’s Click handler (this is the code behind the button named Test Equals Method on the test form).
Listing 8.9: Experimenting with Class Instances
Dim obj1 As New Minimal()
Obj1.property1 = “ABCDEFGHIJKLMNOPQRSTUVWXYZ” Dim obj2 As New Minimal()
obj2 = obj1
If obj1.Equals(obj2) Then Console.WriteLine(“They’re equal”) obj2.property1 = “abcdefghijklmnopqrstuvwxyz”
If obj1.Equals(obj2) Then Console.WriteLine(“They’re equal”)
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |