Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
(ebook) Visual Studio .NET Mastering Visual Basic.pdf
Скачиваний:
120
Добавлен:
17.08.2013
Размер:
15.38 Mб
Скачать

354 Chapter 8 BUILDING CUSTOM CLASSES

Listing 8.19: Initiating the Scanning of a Folder

Protected Sub Button2_Click(ByVal sender As Object, ByVal e As System.EventArgs) Dim folder As Directory

objDirScanner = New DirScanner() folder = New Directory(“D:\”) MsgBox(“Your files occupy “ & _

objDirScanner.ScanTimerFolder(folder).Tostring & “ bytes on the drive”) End Sub

The new event will be caught by the same object, the objDirScanner object, in addition to the ScanProgress event. Delete (or comment out) the statements in the objDirScanner_ScanProgress event handler and enter Listing 8.20’s lines in the new event’s handler.

Listing 8.20: The ScanTimerProgress Event

Public Sub objDirScanner_ScanProgress(ByVal foldersScanned As System.Integer, _ ByRef Cancel As Boolean) Handles objDirScanner.ScanProgress

Me.Text = “Scanned “ & foldersScanned.ToString & “ folders so far” If foldersScanned > 3000 Then Cancel = True

End Sub

To test the Cancel argument, the program sets it to True to terminate the execution of the ScanFolder() method if it has already scanned more than 3,000 files. Normally, you should provide a Cancel button on your form, which the user can click to terminate the execution of the method. Check out the DirScanner project and experiment with other techniques for handling the Cancel argument. If your Program Files folder contains fewer than 3,000 files, use a smaller value in the code to terminate the process of scanning a folder prematurely. Notice that if the scanning operation is interrupted prematurely, the corresponding method will not return the number of folders scanned or the total number of bytes they occupy on disk.

Asynchronous Operations

An asynchronous operation is an operation you initiate from within your code and then continue with other tasks, without waiting for the operation’s completion. When you start the download a file in Internet Explorer, for example, you can continue surfing while the file is being downloaded in the background. The more operations you can perform in the background, the more responsive your application appears to be.

When the operation completes, the class must notify the application that it’s done, and this takes place through an event. These events are similar to the progress events discussed in the previous section, and we won’t discuss them in this chapter.

Shared Properties

When you instantiate a class, its code is loaded into memory, its local variables are initialized, and then the New subroutine is executed. This happens the first time you instantiate a variable of the

Copyright ©2002 SYBEX, Inc., Alameda, CA

www.sybex.com

BUILDING THE MINIMAL CLASS 355

class’s type. If the class has already been instantiated (that is, if you have already created a variable of the same type), the code isn’t loaded again. Instead, a new copy of each local variable is created. The same code acts on different data, and it appears as if you have multiple instances of the class loaded and running at the same time. Each instance of the class has its own properties; the values of these properties are local to each instance of the class. If you declare two variables of the Minimal type in your application, thus:

Dim obj1, obj2 As Minimal

then you can set their Age property to different values:

obj1.property1 = 10 obj2.property2 = 90

The two expressions are independent of one another, as if there were two instances of the class in memory at the same time.

There are situations, however, where 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 in order to enable the class, and this method signals that another user has requested your class. This could be a method that establishes a connection to a database or opens 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 application 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 is called shared and is 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 with the Users property, and only the Connect and Disconnect methods can change its value. Listing 8.21 is the code you must add to the Minimal class to implement a shared property.

Listing 8.21: Implementing a Shared Property

Shared LoggedUsers As Integer

ReadOnly Property Users() As Integer

Get

Users = LoggedUsers

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

Copyright ©2002 SYBEX, Inc., Alameda, CA

www.sybex.com

356 Chapter 8 BUILDING CUSTOM CLASSES

To test the shared variable, add a new button to the form and enter Listing 8.22 in its Click event handler. (The lines in bold are the values reported by the class; they’re not part of the listing.)

Listing 8.22: Testing the LoggedUsers Shared Property

Protected Sub Button5_Click(ByVal sender As Object, _ ByVal e As System.EventArgs)

Dim obj1 As New Minimal() obj1.Connect() Console.WriteLine(obj1.Users)

1

obj1.Connect()

Console.WriteLine(obj1.Users)

2

Dim obj2 As New Minimal() obj2.Connect() Console.WriteLine(obj1.Users)

3

Console.WriteLine(obj2.Users)

3

Obj2.Disconnect()

Console.WriteLine(obj2.Users)

2

End Sub

If you run the application, you’ll see the values displayed under each Console.WriteLine statement in the Output window. The values in bold are not part of the listing; I’ve inserted them in the listing to help you match each item of the output to the statement that produces it. As you can see, both 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 service multiple 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.

A “Real” Class

In this section, I’ll discuss 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 ExtractFileName methods, which extract the file and path name from a full filename. If the full name of a file

is “c:\Documents\Recipes\Chinese\Won Ton.txt”, the ExtractPathName method will return the

substring “c:\Documents\Recipes\Chinese\” and the ExtractFileName method will return the sub-

string “Won Ton.txt”.

Copyright ©2002 SYBEX, Inc., Alameda, CA

www.sybex.com

A “REAL” CLASS 357

Note You can use the Split method of the String class to extract all the parts of a delimited string. Extracting the path name 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 13.

The third method is called Num2String; it converts a numeric value (an amount) to the equivalent string. For example, it can convert the amount $12,544 to the string “Twelve Thousand, Five Hundred And Forty Four.” No other class in the Framework provides this functionality, and any program that prints checks can use this class.

Parsing a Filename String

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 8.23 in the Class module.

Listing 8.23: The ExtractFileName and ExtractPathName Methods

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 Function

These are two simple functions that parse the string passed as argument. If the string contains no delimiter, it’s assumed that the entire argument is just a filename.

The Num2String method is far more complicated, but if you can implement it as a regular function, it doesn’t take any more effort to turn it into a method. The listing of Num2String is shown in Listing 8.24. First, it formats the billions in the value (if the value is that large), then the millions, thousands, units, and finally the decimal part, which may contain no more than two digits.

Copyright ©2002 SYBEX, Inc., Alameda, CA

www.sybex.com

358 Chapter 8 BUILDING CUSTOM CLASSES

Listing 8.24: 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 Num2String = “***”

Exit Function End If

biln = CInt(number / 1000000000)

If biln > 0 Then strNumber = FormatNum(biln) & “ Billion” & Pad() miln = Int((number - biln * 1000000000) / 1000000)

If miln > 0 Then _

strNumber = strNumber & FormatNum(miln) & “ Million” & Pad() thou = Int((number - biln * 1000000000 - miln * 1000000) / 1000) If thou > 0 Then _

strNumber = strNumber & FormatNum(thou) & “ Thousand” & Pad() hund = Int(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 <> Int(number) Then

strNumber = strNumber & FormatDecimal(CInt((number - Int(number)) * 100)) Else

strNumber = strNumber & “ dollars” End If

Num2String = Delimit(SetCase(strNumber)) End Function

Each group of three digits (million, thousand, and so on) is formatted by the FormatNum() function. Then, the appropriate string is appended (“Million”, “Thousand”, and so on). The FormatNum() function, which converts a numeric value less than 1,000 to the equivalent string, is shown in Listing 8.25.

Listing 8.25: 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 = Int(num / 100)

If digit100 > 0 Then strNum = Format100(digit100) digit10 = Int((num - digit100 * 100))

If digit10 > 0 Then

If strNum <> “” Then

Copyright ©2002 SYBEX, Inc., Alameda, CA

www.sybex.com

A “REAL” CLASS 359

strNum = strNum & “ And “ & Format10(digit10) Else

strNum = Format10(digit10) End If

End If

FormatNum = strNum End Function

The FormatNum() function formats a three-digit number as a string. To do so, it calls the Format100() to format the hundreds, and the Format10() function formats the tens. The Format10() function, as you may have guessed, calls the Format1() function to format the units. I will not show the code for these functions; you can find it on the CD 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, like the enumerations used by the class as property values.

To make the Num2String method more flexible, the class exposes the UseCase, UseDelimiter, and UsePadding properties. The UseCase property determines the case of the characters in the string returned by the method. The UseDelimiter method specifies the special characters that may appear before and after the string. Finally, the UsePadding property specifies the character that will appear between groups of digits. The values each of these properties may take on are shown here:

UsePadding

UseDelimiter

UseCase

clsToolsCommas

clsToolsNone

clsToolsCaps

clsToolsSpaces

clsTools1Asterisk

clsToolsLower

clsToolsDashes

clsTools3Asterisks

clsToolsUpper

The actual numeric values are of no interest. The values under each property name are implemented as enumerations, and you need not memorize their names. As you enter the name of property followed by the equal sign, the appropriate list of values will pop up and you can select the desired member.

Listing 8.26 presents the clsToolsCase enumeration and the implementation of the UseCase property:

Listing 8.26: The clsToolsCase Enumeration and the UseCase Property

Enum clsToolsCase clsToolsCaps clsToolsLower clsToolsUpper

End Enum

Private varUseCase As clsToolsCase Public Property UseCase() As clsToolsCase

Get

Return (varUseCase) End Get

Copyright ©2002 SYBEX, Inc., Alameda, CA

www.sybex.com

360 Chapter 8 BUILDING CUSTOM CLASSES

Set

varUseCase = Value

End Set

End Property

Once 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, whose type is clsToolsPadding. The Pad() function uses the members of the clsToolsPadding enumeration to make the code easier to read. As soon as you enter the Case keyword, the list of values that may 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 mvarUsePadding

Case clsToolsPadding.clsToolsSpaces : Pad = “ “

Case clsToolsPadding.clsToolsDashes : Pad = “-”

Case clsToolsPadding.clsToolsCommas : Pad = “, “

End Select

End Function

To test the StringTools class, create a test form like the one shown in Figure 8.9. Then enter the code from Listing 8.27 in the Click event handler of the two buttons.

Figure 8.9

The test form of the

StringTools class

Listing 8.27: Testing the StringTools Class

Protected Sub Button1_Click(ByVal sender As Object, _ ByVal e As System.EventArgs)

Dim objStrTools As New StringTools()

objStrTools.UseCase = StringTools.clsToolsCase.clsToolsCaps objStrTools.UseDelimiter = StringTools.clsToolsDelimit.clsToolsNone objStrTools.UsePadding = StringTools.clsToolsPadding.clsToolsCommas TextBox2.Text = objStrTools.Num2String(CDec(TextBox1.text))

End Sub

Protected Sub Button2_Click(ByVal sender As Object, _ ByVal e As System.EventArgs)

Dim objStrTools As New StringTools()

Copyright ©2002 SYBEX, Inc., Alameda, CA

www.sybex.com