Chapter 7. Introduction to VB.NET

VB.NET or VB7 has much the same basic syntax as earlier versions of Visual Basic, but it is in many ways a completely new language. Unlike previous versions of VB, VB7 is completely object oriented, and many common operations are implemented a little differently because of this difference. For these reasons, it is best to consider VB.NET a language for developing new .NET applications, rather than as a new compiler for programs you have already written. Because of the awkward typography of VB.NET, we'll use the name VB7 to mean the same thing as VB.NET when we refer to it within the text. We'll maintain the VB.NET name in subheads. We'll see some of the advantages of VB7 in this chapter, and in later chapters we'll see how it makes some of the design patterns that much easier to construct useful object-oriented VB programs.

Syntax Differences in VB.NET

The major differences you will find in this version of VB is that all calls to subroutines and class methods must be enclosed in parentheses. In VB6, we could write the following.

Dim myCol As New Collection
MyCol.Add "Mine"

However, in VB7 you must enclose the arguments in parentheses:

Dim myCol As New ArrayList
MyCol.Add ("Mine")

One other significant difference—and for most people an improvement—is that arguments passed into subroutines are by default passed by value instead of by reference. In other words, you can operate on the variable within the subroutine without inadvertently changing its value in the calling program. Thus, the ByVal modifier is now the default. In fact, the development environment inserts it automatically in most cases. If you want to change the value in the calling program, you can still declare an argument using the ByRef modifier instead.

Three other keywords have also been removed or significantly changed from VB6: Set, Variant, and Wend. In fact, the development environment simply removes the Set verb from the beginning of any line where you use it.

Table 7-1. Some syntax differences between VB6 and VB7.

VB6 VB7
Set q = New Collection q = New Collection
Dim y as Variant Dim y as Object
While x < 10
  x = x + 1
Wend
While x < 10
  x = x + 1
End While
Dim x as Integer, y as integer Dim x, y As Integer
ReDim X(30) As Single
Dim X(30) as Single
X = New Single(40)

or

ReDim X(40)

The Dim statement now allows you to list several variables of the same type in a single statement.

Dim x, y As Integer

But you can also list variables of different types in a single statement.

Dim X as Integer, Y As Single

You can also, of course, list them on separate lines.

Dim X as Integer           'legal in both vb6 and vb7
Dim Y as Single

These changes are summarized in Table 7-1.

In addition, the string functions Instr, Left, and Right have been supplemented by the more versatile indexOf and substring methods of the String class.

Note that string indexes are zero based when using these new methods (see Table 7-2).

Table 7-2. String functions in VB6 and 7 compared with String methods.

VB6 or VB7 VB7
Instr(s, ",") s.indexOf(",")
Left(s, 2) s.substring(0,2)
Right(s, 4) s.substring(s.Length() -4)

Improved Function Syntax

One of the awkward bugaboos in VB has been the need to refer to the function name in returning a value from a function.

Public Function Squarit(x as Single)
  Squarit = x * x
End Function

In VB7, this restriction is finally lifted, and you can simply use the return statement, as is common in many other languages.

Public Function Squarit(x as Single)
    Return x * x
End Function

This makes functions much simpler to type and use.

Variable Declarations and Scoping

VB7 normally requires that you declare all variables with a Dim statement before you use them. While it is possible to turn this off, it is certainly unwise because of the additional error checking protection it provides.

In addition, VB7 allows you to declare a variable and assign a value to it at the same time:

Dim age As Integer = 12

In fact, this changes the whole style of programming and variable declaration, because you can declare a variable right where you use it instead of at the top of the subroutine. In the following example we sort the elements of the array a.

For i = 1 To UBound(a)
    Dim j As Integer
    For j = i + 1 To UBound(a)
        If (a(i) > a(j)) Then
            Dim temp As Single
            temp = a(i)
            a(i) = a(j)
            a(j) = temp
        End If
    Next j
Next i
'error! temp no longer exists
Console.WriteLine("temp=" + temp)

We declare a variable j inside the outer loop, and a variable temp inside the inner loop. These variables only have existence inside that block and are “forgotten” by the compiler at the end of that block. Thus, you cannot refer to the variable j outside the j-loop and cannot refer to the temp variable outside the inner loop. The Console.WriteLine statement results in a compiler error, since temp no longer exists outside the inner loop.

You cannot declare a variable at two different block levels in VB7. Thus, if you declare a variable in an outer loop, you can't redeclare a new version of it in an inner block enclosed by that outer block.

Dim temp As Single  'outer declaration

For i = 1 To UBound(a)
    Dim j As Integer
    For j = i + 1 To UBound(a)
        If (a(i) > a(j)) Then
            'error! temp already longer exists
            Dim temp As Single
            temp = a(i)
            a(i) = a(j)
            a(j) = temp
        End If
    Next j
Next i

However, you can declare the same variable over again in a new block once the program has exited from the previous block.

For i = 1 To UBound(a)
            Dim j As Integer 'legal
            For j = i + 1 To UBound(a)
           'some code
            Next j
       Next i
       Dim j As Integer 'legal

Objects in VB.NET

In VB7, everything is treated as an object. While in VB6, you can create class instances that behave as objects. Objects contain data and have methods that operate on them. In VB7, this is true of every kind of variable.

Strings are objects, as we just illustrated. They have methods such as these.

Substring
ToLowerCase
ToUpperCase
IndexOf
Insert

However, strings do not at the moment have methods for converting to numerical values. Instead, you can use the VB Cint, CSng and CDbl functions for this purpose.

Integers and Single and Double variables are also objects, and they have methods as well.

Dim s as String
Dim x as Single

x =  12.3
s = x.ToString             'convert to String
x = CSng(s)                'convert to Single

Note that conversion between strings and numerical types is better done using these methods rather than using the Val and Str functions. If you want to format a number as a particular kind of string, each numeric type has a Format method.

Dim s as String
Dim x as Single
x = 12.34

s = Single.Format(x, "##.000")

Compiler Options

If you are familiar with VB6, you probably know about the compiler Option Explicit directive. This requires that you declare all variables with Dim statements before using them. VB7 introduces the Option Strict On statement. Option Strict forces a higher level of compiler checking, and restricts automatic conversion between wider and narrower variable types. The default setting in VB7 is Option Explicit, but you can turn on Option Strict for a whole project by right clicking on the Project name in the Project Explorer window. Then select Build and select On from the Option Strict drop-down selector as shown in Figure 7-1.

Setting Option Strict in the Property pages dialog

Figure 7-1. Setting Option Strict in the Property pages dialog

Numbers in VB.NET

All numbers without decimal points are assumed to be of type Integer or Int32 (a 32-bit signed integer), and all numbers with decimal points are assumed to be of type Double.

Normally the VB.NET compiler is set to Option Explicit, which prevents most undeclared type conversions, except to wider types. You can always convert from integer to single or double. However, if you want to convert a single to an integer, you must specifically indicate that this is what you want to do, using the CInt, CSng, CDbl, CLng, CDate, and CStr functions.

Dim k As Integer = CInt(time)
Dim time as Single = CSng("3.3")

The Currency type is no longer available in VB7. Instead, you can use the Decimal type, which provides a high precision.

VB7 also provides the Convert class, which has a number of shared methods for converting between types.

Dim k As Integer = Convert.ToInt32(s)

Properties in VB6 and VB.NET

Visual Basic provides a construct called properties that is analogous to the getXxx and setXxx methods of other languages. In VB6, you can specify a property by name and define its Get and Let methods. These two methods allow you to set the value of a private variable and return the value of that variable.

Property Get fred() As String
  fred = fredName
End Property

Property Let fred(s As String)
  fredName = s
End Property

Of course, you can do pre- and postprocessing of these data to validate them or convert them from other forms as needed.

In VB7, these properties are combined into a single routine.

Property Fred() As String
    Get
        Return fredName
    End Get
    Set
        fredName = value
    End Set
End Property

Note the special keyword value. We use it in VB7 to indicate the value being passed in to a property. So if we write the following

Abc.Fred = "dog"

then the string value will contain “dog” when the property Set code is executed.

In both systems, the purpose is to provide a simple interface to get and set values from a class without knowing how the data are actually stored. You use these properties in common assignment statements. If “Fred” is a property of the Boy class, then you can write statements like this.

Dim kid as Boy
Kid.fred = "smart"

And you can write this.

Dim brain as string
Brain = kid.fred

In general, the Property system provides an alternate syntax to the getFred and setFred functions that you could write just as easily. While the syntax differs, there is no obvious advantage except that many native VB objects have properties rather than get and set methods. In this book we will not make much use of properties because they are not a significant advantage in coding object-oriented programs.

Shorthand Equals Syntax

VB7 adopts the shorthand equals syntax we find in C, C++, C#, and Java. It allows you to add, subtract, multiply, or divide a variable by a constant without typing its name twice.

Dim i As Integer = 5
        i += 5  'add 5 to i
        i -= 8  'subtract 8 from i
        i *= 4  'multiply i by 4

You can use this approach to save typing, but the code generated is undoubtedly the same as if you had written the code out in the old way

i = i + 5
i = i - 8
i = i * 4

The same applies to division, but you seldom see it because it is awkward to read.

Managed Languages and Garbage Collection

VB.NET and C# are both managed languages. This has two major implications. First, both are compiled to an intermediate low-level language, and a common language runtime (CLR) is used to execute this compiled code, perhaps first compiling it further. So not only do VB7 and C# share the same runtime libraries, they are to a large degree two sides of the same coin and two aspects of the same language system. The differences are that VB7 is more Visual Basic-like and a bit easier to learn and use. C# on the other hand is more C++ and Java-like, and it may appeal more to programmers already experienced in those languages.

The other major implication is that managed languages are garbage collected. Garbage-collected languages take care of releasing unused memory: You never have to be concerned with this. As soon as the garbage-collection system detects that there are no more active references to a variable, array, or object, the memory is released back to the system. So you no longer need to worry as much about running out of memory because you allocated memory and never released it. Of course, it is still possible to write memory-eating code, but for the most part you do not have to worry about memory allocation and release problems.

Classes in VB.NET

Classes are a very important part of VB7. Almost every important program consists of one or more classes. The distinction between classes and forms has disappeared in VB7, and most programs are all classes. Since nearly everything is a class, the number of names of class objects can get to be pretty overwhelming. They have therefore been grouped into various functional libraries that you must specifically mention in order to use the functions in these libraries.

Under the covers these libraries are each individual DLLs. However, you need only refer to them by their base names, using the Imports statement, and the functions in that library are available to you.

Imports System.IO      'Use File namespace classes

Logically, each of these libraries represents a different namespace. Each namespace is a separate group of class and method names that the compiler will recognize after you import that name space. You can import namespaces that contain identically named classes or methods, but you will only be notified of a conflict if you try to use a class or method that is duplicated in more than one namespace.

You can specify which namespaces you want to import into every class in your project using the Property pages dialog, as shown in Figure 7-2.

Setting the Namespaces to import using the Property pages. The names shown are set by default.

Figure 7-2. Setting the Namespaces to import using the Property pages. The names shown are set by default.

The most common namespace is the System namespace, and it is imported by default without your needing to declare it. It contains many of the most fundamental classes and methods that VB7 uses for access to basic classes, such as Application, Array, Console, Exceptions, Objects, and standard objects, such as Byte, Boolean, Single and Double, and String. In the simplest VB7 program we can simply write “hello” out to the console without ever bringing up a window or form.

'Simple VB Hello World program
Public Class cMain
   Shared Sub Main()    'entry point is Main
      'Write text to the console
      Console.WriteLine ("Hello VB World")
   End Sub
End Class

This program just writes the text “Hello VB World” to a command (DOS) window. The entry point of any program must be a Sub Main subroutine, and in a class module, it must be declared Shared. The only other type of module in VB7 is the Module type. Here the program can be written like this.

'Simple VB Hello World program
Public Module cMain
   Sub Main()    'entry point is Main
      'Write text to the console
      Console.WriteLine ("Hello VB World")
   End Sub
End Module

The programs are pretty much identical, except that a Module has all public (and shared) methods and the Sub Main need not be declared as Shared. Modules are analogous to the Modules in earlier versions of VB and are the ones that Vb would have created with a .bas file type. They have the advantage that all of the methods and constants declared in a Module are public and can be referenced throughout the program. However, unlike classes, they provide no way to hide information or algorithms, and we will not use them further in this book.

In VB6, the class name was usually declared the filename, although you could change a class's Name property. In VB7, the Class keyword allows you to declare the class name irrespective of the filename, and in fact, you must declare a class name for each class. The default file extensions for VB6 classes was .cls and for forms .frm. In VB7, you can use any filename or extension you want, but the default file extension is .vb.

Building a VB7 Application

Let's start by creating a simple console application: one without any windows that just runs from the command line. Start the Visual Studio.NET program, and select File|New Project. From the selection box, choose Console application, as shown in Figure 7-3.

The New Project selection window. Selecting a console application

Figure 7-3. The New Project selection window. Selecting a console application

This will bring up a module, with the Sub Main already filled in. You can type in the rest of the code as follows.

Module cMain
    Sub Main()
        'write text to the console
        Console.WriteLine("Hello VB world")
    End Sub
End Module

You can compile this and run it by pressing F5. If you change the program's main module name from Module1 to cMain, as we did here, you will also have to change the name of the Startup module. To do this, in the right-hand Solution Explorer window, right-click on the project name and select Properties from the pop-up menu. This will appear as in Figure 7-4.

The property page for the project

Figure 7-4. The property page for the project

You can change the startup object in the drop-down menu to the correct new name. When you compile and run the program by pressing F5, a DOS window will appear and print out the message “Hello VB World” and exit.

You can also delete the module and insert a class instead.

Public Class cMain
    Shared Sub Main()
        Console.WriteLine("Hello classy VB world")
    End Sub
End Class

This will compile and run in just the same way.

The Simplest Window Program in VB.NET

It is just about as simple to write a program that brings up a window. In fact, you can create most of it using the Windows Designer. To do this, start Visual Studio.NET and select File|New project, and then select Windows Application. The default name (and filename) is WindowsApplication1, but you can change this before you close the New dialog box. This brings up a single form project, initially called Form1.vb. You can then use the Toolbox to insert controls, just as you could in VB6.

The Windows Designer for a simple form with two labels, one text field and one button, is shown in Figure 7-5.

The Windows Designer in Visual Studio.NET

Figure 7-5. The Windows Designer in Visual Studio.NET

You can draw the controls on the form and double click on the controls to enter code. In this simple form, we click on the “Say hello” button, and it copies the text from the text field to the blank label we named lbHi and clears the text field.

Protected Sub SayHello_Click(ByVal sender As Object, _
                         ByVal e As System.EventArgs)
        lbhi.Text() = txhi.Text
        txhi.Text = ""
End Sub

The code it generates is a little different. Note that the Click routine passes in the sender object and an event object that you can query for further information. The running program is shown in Figure 7-6.

The SimpleHello form before and after clicking the Say Hello button

Figure 7-6. The SimpleHello form before and after clicking the Say Hello button

While we only had to write the two lines of code inside the preceding subroutine, it is instructive to see how different the rest of the code is for this program.

Inheritance

Next, we see the most stunning change in VB7- inheritance.

Public Class HelloForm
    Inherits System.Windows.Forms.Form

The form we create is a child class of the Form class, rather than being an instance of it as was the case in previous versions of VB. This has some very powerful implications. You can create visual objects and override some of their properties so each behaves a little differently. We'll see some examples of this shortly.

The code the designer generates for the controls is illuminating. No longer is the code for the control creation buried in an interpreter of Form module declarations you can't easily change. Instead, it is right there in the open for you to change if you want. Note, however, that if you change this code manually instead of using the property page, the window designer may not work anymore. That is why this section is initially collapsed inside a “[+]” box line on the code display.

Essentially, each control is declared as a variable and added to a container. Here are the control declarations.

Friend WithEvents txHi As System.Windows.Forms.TextBox
Friend WithEvents lbHi As System.Windows.Forms.Label
Friend WithEvents Button1 As System.Windows.Forms.Button
    'Required by the Windows Form Designer
    Private components As System.ComponentModel.Container
    <System.Diagnostics.DebuggerStepThrough()>
Private Sub InitializeComponent()
        Me.txHi = New System.Windows.Forms.TextBox()
        Me.lbHi = New System.Windows.Forms.Label()
        Me.Button1 = New System.Windows.Forms.Button()
        Me.SuspendLayout()
        '
        'txHi
        '
        Me.txHi.Location = New System.Drawing.Point(48, 24)
        Me.txHi.Name = "txHi"
        Me.txHi.Size = New System.Drawing.Size(144, 20)
        Me.txHi.TabIndex = 0
        Me.txHi.Text = "Hello"
        '
        'lbHi
        '
        Me.lbHi.Font = New System.Drawing.Font(
              "Microsoft Sans Serif", 14.25!,
              System.Drawing.FontStyle.Regular,
              System.Drawing.GraphicsUnit.Point, CType(0, Byte))
        Me.lbHi.ForeColor = System.Drawing.Color.FromArgb(
              CType(0, Byte), CType(0, Byte), CType(192, Byte))
        Me.lbHi.Location = New System.Drawing.Point(48, 64)
        Me.lbHi.Name = "lbHi"
        Me.lbHi.Size = New System.Drawing.Size(152, 24)
        Me.lbHi.TabIndex = 1
        '
        'SayHello
        '
        Me.SayHello.Location =
              New System.Drawing.Point(88, 136)
        Me.SayHello.Name = "SayHello"
        Me.SayHello.Size = New System.Drawing.Size(80, 24)
        Me.SayHello.TabIndex = 2
        Me.SayHello.Text = "Hello"
        '
        'Form1
        '
        Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
        Me.ClientSize = New System.Drawing.Size(292, 273)
        Me.Controls.AddRange(
        New System.Windows.Forms.Control()
                       { Me.Button1, Me.lbHi, Me.txHi} )
        Me.Name = "Form1"
        Me.Text = "Say hello"
        Me.ResumeLayout(False)


    End Sub

Note that the SayHello button is declared using the WithEvents modifier. This means that there can be a direct connection between the button and the subroutine SayHello_Click.

Constructors

All classes now have specific constructors that are called when you create an instance of a class. These constructors are always named New. This applies to form classes as well as nonvisual classes. Here is the constructor for our simple hello window.

Public Sub New()
  MyBase.New()
 'This call is required by the Windows Form Designer.
  InitializeComponent()
 'Add any initialization after the InitializeComponent() call
End Sub

Note the MyBase.New method call. This calls the constructor of the parent class and initializes it. Code to accomplish this is generated even if you leave this line out.

Many times, we'll need to initialize variables or other classes as part of our program startup. The place we would insert that code is at the bottom of the New method shown above. However, since the New method is usually hidden inside the collapsed +-sign area, we'll adopt the convention that our New method will always call an init() method and place that call at the bottom of the New method. Then we can place the init method in plain sight. Here is a trivial example:

    Private Sub init()
        lbHi.Text = "..."
    End Sub
#Region " Windows Form Designer generated code "

Public Sub New()
    MyBase.New()
        'This call is required by the Windows Form Designer.
        InitializeComponent()
'Add any initialization after the InitializeComponent() call
        init()
End Sub

When you create your own classes, you can create New methods to initialize them and can pass arguments into the class to initialize class parameters to specific values. Suppose you wanted to create a StringTokenizer class like the one we defined in Chapter 3.

The VB7 String class has a Split method that is analogous to VB6's Split function that returns an array of strings. The difference is that you pass it an array of characters for the possible separators instead of a String.

sarray = s.Split(sep.ToCharArray)

We'll use this method in our VB7 tokenizer class.

Here we use real constructors instead of the init() method. Our constructor will copy the string into an internal variable and create a default value for the token separator.

Public Class StringTokenizer
    Private s As String
    Private i As Integer
    Private sep As String   'token separator
    Private sArray() As String
    '------
    Public Sub New(ByVal st As String)
        s = st         'copy in string
        set_Separator(" ")        'default separator
    End Sub
    '------
    Public Sub New(ByVal st As String, _
            ByVal sepr As String)
        s = st
        set_Separator(sepr) 'creates the array
    End Sub
    '-----
    Private Sub set_separator(ByVal sp As String)
        sep = sp      'copy separator
        'and create the array
        sarray = s.Split(sep.ToCharArray)
        i = 0
    End Sub
    '------
    Public Sub setSeparator(ByVal sp As String)
        set_separator(sp)
    End Sub
    '------
    Public Function nextToken() As String
        Dim j As Integer
        j = i
        i = i + 1
        If j < sarray.Length Then
            Return sarray(j)
        Else
            Return ""
        End If
    End Function
End Class

Our calling program simply creates an instance of the tokenizer and prints out the tokens as a console application.

'illustrates use of tokenizer
Public Class TokTest
    Shared Sub Main()
        Dim s As String
        Dim tok As New StringTokenizer("Hello VB World")
        s = tok.nextToken()
        While (s <> "")
            Console.writeLine(s)
            s = tok.nextToken()
        End While
    End Sub
End Class

Note that VB7 allows you to declare variables and initialize them in the same statement.

Dim tok As New StringTokenizer("Hello VB World")

Drawing and Graphics in VB.NET

In VB7, controls are repainted by the Windows system, and you can override the OnPaint event to do your own drawing. The PaintEventArgs object is passed into the subroutine by the underlying system, and you can obtain the graphics surface to draw on from that object. To do drawing, you must create an instance of a Pen object and define its color and, optionally, its width. This is illustrated here for a black pen with a default width of 1.

Protected Overrides Sub OnPaint(ByVal e as PaintEventArgs)
  Dim g as Graphics = e.Graphics
  Dim rpen As new Pen(Color.Black)
  g.drawLine(rpen, 10,20,70,80)
End Sub

The Overrides keyword is a critical part of the VB inheritance system. Using this keyword tells the compiler that you are overriding the same method in a parent class.

Tooltips and Cursors

A Tooltip is a message that appears when you allow the mouse cursor to hover over a control. In VB6, each control had a Tooltip property. In VB7, you create an instance of the Tooltip object for each form, and add the controls to it along with the message you wish to display.

Dim tips As New ToolTip()
tips.SetToolTip(Button1, "Click to Execute")

A cursor is the visual indication of the mouse position. Its default image is an arrow. There can only be one cursor displayed on a form and you set it using the shared Current property of the Cursor object. You can select the type of cursor from the public shared constants in the Cursors object:

Cursor.Current = Cursors.WaitCursor
Cursor.Current = Cursors.Default

The usual list of constant cursor names includes:

Cursors.Arrow
Cursors.Cross
Cursors.Default
Cursors.Ibeam
Cursors.No
Cursors.SizeAll
Cursors.UpArrow
Cursors.WaitCursor

and so forth.

Overloading

In VB7, as well as other object-oriented languages, you can have several class methods with the same name as long as they have different calling arguments or signatures. For example, we might want to create an instance of the StringTo kenizer where we define both the string and the separator.

tok = New StringTokenizer("apples, pears", ",")

If we want to implement this constructor, we overload the constructor and the compiler will know we have two methods with the same name, but different arguments. Here are the two constructors.

Public Sub New(st as String, sepr as String)
  s = st
  sep = sepr
End Sub
'------
Public Sub New(st As String)
 s = st         'copy in string
 sep = " "      'default separator
End Sub

VB allows us to overload any other method as long as we provide arguments that allow the compiler to distinguish between the various overloaded (or polymorphic) methods. You can use the OverLoads keyword to point out that you have two or more methods with the same name, but different arguments for any Sub of Function you create. However, you do not use OverLoads with New methods, and the compiler flags the use of the Overloads keyword with New methods as an error.

Inheritance

The most powerful new feature in VB7 is the ability to create classes that are derived from existing classes. In new derived classes, we only have to specify the methods that are new or changed. All the others are provided automatically from the base class from which we inherit. To see how this works, let's consider writing a simple Rectangle class that draws itself on a form window. This class has only two methods, the constructor and the draw method.

Namespace VBPatterns
Public Class Rectangle
    Private x, y, h, w As Integer
    Protected rpen As Pen
    '------
Public Sub New(ByVal x_ As Integer, _
                  ByVal y_ As Integer, _
                  ByVal h_ As Integer, _
                  ByVal w_ As Integer)
     x = x_
     y = y_
     h = h_
     w = w_
     rpen = New Pen(Color.Black)
  End Sub
  '--------
  Public Sub draw(ByVal g As Graphics)
      g.DrawRectangle(rpen, x, y, w, h)
  End Sub
 End Class
End Namespace

Namespaces

We mentioned the System namespaces previously. VB7 creates a Namespace for each project equal to the name of the project itself. You see this default namespace being generated in Figure 7-4. You can change this namespace on the property page or make it blank so that the project is not in a namespace. However, you can create namespaces of your own, and the Rectangle class provides a good example of a reason for doing so. There already is a Rectangle class in the System.Drawing namespace that this program imports. Rather than renaming the class to avoid this name overlap or “collision,” we can just put the whole Rectangle class in its own namespace by wrapping the class inside a namespace declaration, as we just showed.

Then, when we declare the variable in the main Form window, we declare it as a member of that namespace.

Public Class Rect_Form
    Inherits System.Windows.Forms.Form

    Private rect As VBPatterns.Rectangle

In this main Form window's init method (which we call from within New as before), we create an instance of our Rectangle class.

Private Sub init()
        rect = New VBPatterns.Rectangle(20, 50, 100, 200)
End Sub

Then we override the OnPaint event to do the drawing and pass the graphics surface on to the Rectangle instance.

Protected Overrides Sub OnPaint( _
            ByVal e As PaintEventArgs)
        Dim g As Graphics
        g = e.Graphics
        rect.draw(g)
End Sub

This gives us the display we see in Figure 7-7.

The Rectangle drawing program

Figure 7-7. The Rectangle drawing program

Creating a Square from a Rectangle

A square is just a special case of a rectangle, and we can derive a Square class from the Rectangle class without writing much new code. Here is the entire class.

Namespace VBPatterns
    Public Class Square
        Inherits Rectangle
        Public Sub New(ByVal x As Integer, _
            ByVal y As Integer, ByVal w As Integer)
            MyBase.New(x, y, w, w)
        End Sub
    End Class
End Namespace

This Square class contains only a constructor, which passes the square dimensions on to the underlying Rectangle class by calling the constructor of the parent Rectangle class. The Rectangle class creates the pen and does the actual drawing. Note that there is no draw method at all for the Square class. If you don't specify a new method, the parent class's method is used automatically.

The program that draws both a rectangle and a square has a simple constructor where instances of these objects are created. It calls the init method below:

Private Sub init()
        rect = New VBPatterns.Rectangle(20, 50, 100, 200)
        sq = New VBPatterns.Square(70, 80, 50)
    End Sub

The program also has an OnPaint routine, where they are drawn.

Protected Overrides Sub OnPaint( _
            ByVal e As PaintEventArgs)
   Dim g As Graphics
   g = e.Graphics
   rect.draw(g)
   sq.draw(g)
End Sub

The display for the square and rectangle is shown in Figure 7-8.

The rectangle class and the square class derived from it

Figure 7-8. The rectangle class and the square class derived from it

Public, Private, and Protected

In VB6, you could declare variables and class methods as either public or private. A public method is accessible from other classes, and a private method is accessible only inside that class. Usually, you make all class variables private and write getXxx and seXxx accessor functions to set or obtain their values. It is generally a bad idea to allow variables inside a class to be accessed directly from outside the class, since this violates the principle of encapsulation. In other words, the class is the only place where the actual data representation should be known, and you should be able to change the algorithms inside a class without anyone outside the class being any the wiser.

VB7 introduces the protected keyword as well. Both variables and methods can be protected. Protected variables can be accessed within the class and from any subclasses you derive from it. Similarly, protected methods are only accessible from that class and its derived classes. They are not publicly accessible from outside the class.

Overriding Methods in Derived Classes

Suppose we want to derive a new class called DoubleRect from Rectangle that draws a rectangle in two colors offset by a few pixels. In the constructor, we create a red pen for doing this drawing.

Namespace VBPatterns
    Public Class DoubleRect
        Inherits Rectangle
        Private redPen As Pen
        '-----
        Public Sub New(ByVal x As Integer, _
                ByVal y As Integer, ByVal w As Integer, _
                ByVal h As Integer)
         MyBase.New(x, y, w, h)
         redPen = New Pen(Color.FromARGB(255, _
                  Color.Red), 2)
        End Sub

This means that our new class DoubleRect must have its own draw method. Now the base class has a draw method, and we really should create a method with the same name, since we want all these classes to behave the same. However, this draw method will use the parent class's draw method but add more drawing of its own. In other words, the draw method will be overridden, and we must specifically declare that fact to satisfy the VB compiler.

Public Overrides Sub draw(ByVal g As Graphics)
    MyBase.draw(g)
    g.drawRectangle(redPen, x + 4, y + 4, w, h)
End Sub

Note that we want to use the coordinates and size of the rectangle that was specified in the constructor. We could keep our own copy of these parameters in the DoubleRect class, or we could change the protection mode of these variables in the base Rectangle class to protected from private.

  Protected x, y, h, w As Integer

We also must tell the compiler that we want to allow the Rectangle's draw method to be overridden by declaring it as overridable.

Public Overridable Sub draw(ByVal g As Graphics)
     g.DrawRectangle(rpen, x, y, w, h)
End Sub

The final rectangle drawing window is shown in Figure 7-9.

The Rectangle, Square, and DoubleRect classes

Figure 7-9. The Rectangle, Square, and DoubleRect classes

Overloading and Shadowing

VB7 provides the keyword Shadows in addition to Overloads. A method or property which overloads the member of the base class simply replaces that particular method name in the derived class. If you have several overloaded versions of that method name, the overloaded one is the only one replaced.

Let's consider the simple class BaseClass shown here:

Public Class BaseClass
    Private data As Single
    '-----
    Public Sub setData(ByVal x As Single)
        data = x
    End Sub
    '-----
    Public Sub setData()
        data = 12.2
    End Sub
End Class

In this class there are two overloaded versions of the setData method, one which requires an argument and one which uses a default internal value. You can also write the same class using the Overloads keyword:

Public Class BaseClass
    Private data As Single
    Private Const defaultv As Single = 12.2
    '-----
    Public Overloads Sub setData(ByVal x As Single)
        data = x
    End Sub
    '-----
    Public Overloads Sub setData()
        data = defaultv
    End Sub
End Class

These have the same meaning and compile to the same code.

Now let's consider a class derived from this one which has a new version of the setData method:

Public Class DerivedClass
    Inherits BaseClass
    '-----
    Public Overloads Sub setData(ByVal x As Single)
        MyBase.setData(x + 12)
    End Sub
End Class

This class has two setData methods: the one that is declared above and the one that is inherited from the BaseClass class. So we can write

Dim cl As New DerivedClass()
        cl.setData()
        cl.setData(14)

On the other hand, if we use the Shadows keyword, then all other overloaded methods of that name in the base class become inaccessible in the derived class.

Public Class DerivedClass
    Inherits BaseClass
    '-----
    Public Shadows Sub setData(ByVal x As Single)
        MyBase.setData(x + 12)
    End Sub
End Class

Then the only version of the setData method that exists in the DerivedClass is the one shown, and the one with no arguments is an error:

Dim cl As New DerivedClass()
        cl.setData()   'now is illegal
        cl.setData(14) 'is legal

Overriding Windows Controls

In VB7 we can finally make new Windows controls based on existing ones, using inheritance. Earlier we created a Textbox control that highlighted all the text when you tabbed into it. In VB6, we did this by writing a new DLL in which the Textbox was enclosed in a Usercontrol and where we passed all the useful events on to the Textbox. In VB7, we can create that new control by just deriving a new class from the Textbox class.

We'll start by using the Windows Designer to create a window with two text boxes on it. Then we'll go to the Project|Add User Control menu and add an object called HiTextBox.vb. We'll change this to inherit from TextBox instead of UserControl.

public class HTextBox
        Inherits Textbox

Then, before we make further changes, we compile the program. The new HiTextBox control will appear at the bottom of the Toolbox on the left of the development environment. You can create visual instances of the HiTextBox on any windows form you create. This is shown in Figure 7-10.

The Toolbox, showing the new control we created and an instance of the HiTextBox on the Windows Designer pane of a new form

Figure 7-10. The Toolbox, showing the new control we created and an instance of the HiTextBox on the Windows Designer pane of a new form

Now we can modify this class and insert the code to do the highlighting.

'A text box that highlights when you tab into it

 Public Class HiTextBox
    Inherits System.windows.forms.TextBox
    Private Sub init()
        AddHandler Enter, _
            New System.EventHandler(AddressOf Me.HT_Enter)
    End Sub

    '------------
    'Enter event handler is inside the class
    Protected Sub HT_Enter(ByVal sender As System.Object, _
            ByVal e As System.EventArgs)
        Me.SelectionStart = 0
        Me.SelectionLength = Me.Text.Length
    End Sub

End Class

And that's the whole process. We have derived a new Windows control in about ten lines of code. That's pretty powerful. You can see the resulting program in Figure 7-11. If you run this program, you might at first think that the ordinary TextBox and the HiTextBox behave the same because tabbing between them highlights both. This is the “autohighlight” feature of the VB7 textbox. However, if you click inside the TextBox and the HiTextBox and tab back and forth, you will see in Figure 7-11 that only our derived HiTextBox continues to highlight.

A new derived HiTextBox control and a regular TextBox control

Figure 7-11. A new derived HiTextBox control and a regular TextBox control

Interfaces

VB7 also continues to support interfaces much like VB6 did. However, the syntax has changed somewhat. Now an Interface is a special kind of class.

Public Interface MultiChoice
    'an interface to any group of components
    'that can return zero or more selected items
    'the names are returned in an Arraylist
    Function getSelected() As ArrayList
    Sub clear()             'clear all selected
    Function getWindow() As Panel
End Interface

When you implement the methods of an Interface in concrete classes, you must declare that the class implements that interface.

Public Class ListChoice
  Implements MultiChoice

And you must also declare that each method of the class implements it as well.

Public Function getSelected() As ArrayList _
         Implements MultiChoice.getSelected

 End Function
'--------
'clear all selected items
Public Sub clear()  implements MultiChoice.clear
End Sub
'--------
Public Function getWindow() As Panel _
        Implements MultiChoice.getWindow
  return pnl
End Function

You can easily insert these methods correctly from the Visual Studio code designer as you can see in Figure 7-12.

Using the IDE to create the methods to implement the MultiChoice interface.

Figure 7-12. Using the IDE to create the methods to implement the MultiChoice interface.

Note that all interfaces must be declared as “Public” or you will get a confusing compiler error about attempting to access Friend classes. We'll show how to use this interface when we discuss the Builder pattern.

Summary

We've seen the shape of most of the new features in VB7 in this chapter. In addition to some syntax changes, VB7 adds inheritance, constructors, and the ability to overload methods to provide alternate versions. This leads to the ability to create new derived versions even of Windows controls. In the chapters that follow, we'll show you how you can write design patterns in both VB6 and VB7.

Programs on the CD-ROM

IntroVBNetHello1 The console “Hello” application using the Module approach
IntroVBNetHello2 The console “Hello” application using the Class approach
IntroVBNetHiText A subclassed text box
IntroVBNetSayHello A Simple Hello program
IntroVBNetTokenizer A string tokenizer
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset