- •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
820 Chapter 18 RECURSIVE PROGRAMMING
I need to mention one important aspect of recursion here. The clr variable is local and maintains its value while the subroutine is interrupted. Visual Basic stores the values of the local variables of the interrupted procedures and recalls them when the procedure gets a chance to complete. This is possible because each new copy of the procedure that starts executing has its own set of local variables. Local variables are part of the procedure’s status.
Scanning Folders Recursively
The examples of recursive functions we have looked at so far probably haven’t entirely convinced you of the usefulness of recursion. The factorial of a number can be easily calculated with a For…Next loop, and the Recurse subroutine is a side effect (basically, a bug). So, what good is recursion?
The answer is the FileScan application, which can’t be implemented non-recursively. I hope that the previous examples helped you understand the principles of recursive programming and that you’re ready for some real recursion. We’ll design an application similar to Windows Explorer, which scans an entire folder, including its subfolders. As the application scans the files and subfolders of a folder or an entire volume, it can locate files by name, size, and date. It can also move files around and, in general, perform all the operations of Windows Explorer plus any other custom operation you might require. Much of the functionality of this application is provided by Windows Explorer already, but as you’ll see shortly, this application is highly customizable. It can serve as your starting point for many file operations that Windows Explorer doesn’t provide. For example, your custom Explorer could expand all the subfolders each time you open a folder, display the full pathname of each folder, or even process files of a certain type (resize image files, encrypt documents, and so on). Later in the chapter, you’ll see an application that generates a list of all the files in a folder, including its subfolders, organized by folder.
A file-scanning application is ideal for implementing with a recursive function because its operation is defined recursively. Suppose you want to scan all the entries of a folder and locate the files whose size exceeds 1 MB or count the files. If an entry is another folder, the same process must be repeated for that subfolder. If the subfolder contains one or more subfolder(s) of its own, the process must be repeated for each subfolder. This application calls for a recursive function, because every time it runs into a subfolder, it must interrupt the scanning of the current folder and start scanning the subfolder by calling itself. If you spend some time thinking about the implementation of this algorithm, you’ll conclude that there’s no simple way to do it without recursion. Actually, once you’ve established the recursive nature of a process, you’ll know you must code it as a recursive procedure.
Describing a Recursive Procedure
When you’re about to write a recursive procedure, it’s helpful to start with a general written description of the procedure. For the FileScan application, we need a subroutine (since it’s not going to return a result) that scans the contents of a folder: let’s call it ScanFolder(). The ScanFolder() subroutine must scan all the entries of the initial folder and process the files in it. If the current entry is a file, it must act upon the file, depending on its name, size, or any of its attributes. If the current entry is a folder, it must scan the contents of this folder. In other words, it must interrupt the
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |
SCANNING FOLDERS RECURSIVELY 821
scanning of the current folder and start scanning the subfolder. And the most efficient way to scan a subfolder is to have it to call itself. Here’s the ScanFolder() function in pseudocode:
Sub ScanFolder(current_folder)
Process files in current_folder
If current_folder contains subfolders
For each subfolder
ScanFolder(subfolder)
Next
End If
Translating the Description to Code
Now, let’s translate this description to actual code. Because we need access to each folder’s files and subfolders, we need three controls to display the drives, the folders of the selected drive, and the files in the selected folder, as shown in Figure 18.5. This is the FileScan project, which you can find in this chapter’s folder on the CD. The drives are displayed on a ComboBox control. When the user selects a drive on this ComboBox, the drive’s folders are displayed on the ListBox control under it. When the user double-clicks a folder’s name, the ListBox control is populated with the subfolders of the selected folder. Finally, when the user clicks the Scan Now button, the code scans the selected folder, including its subfolders. This application is similar to the CustomExplorer application you developed in Chapter 13. I will repeat the process for the benefit of readers who are not familiar with recursive coding, and in the following section we’ll add a unique feature to the application. Where the FileScan application displays the files under the current folder only, we’ll add a button to display all the files in the selected folder, as well as the files in all subfolders under the selected one.
Figure 18.5
The FileScan application is the core of a custom Explorer.
Displaying Drives and Folders
The Drives ComboBox control is populated when the application starts from within the Form_Load event handler with the following statements, shown in Listing 18.3.
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |
822 Chapter 18 RECURSIVE PROGRAMMING
Listing 18.3: Populating the Drives ComboBox Control
Private Sub Form1_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load Dim drives() As String
drives = System.IO.Directory.GetLogicalDrives Dim iDrive As Integer DrivesList.Items.Clear()
On Error Resume Next
For iDrive = 0 To drives.GetUpperBound(0) DrivesList.Items.Add(drives(iDrive))
Next DrivesList.SelectedIndex = 1
End Sub
Notice the error-trapping code, which consists of the On Error Resume Next statement. This is the simplest type of error trapping, but that’s all we need. VB.NET will throw an exception if you attempt to access a drive that’s not ready. This will happen if you have two floppy drives, A: and B:, and there’s no disk in drive B: when you select the second item in the list (on most systems, the second item will be the hard drive C:). The On Error Resume Next statement tells VB to ignore the error and continue. It will add the names of drives A: and B: to the Drives ComboBox control even if it can’t access the selected drive.
When a drive is selected in the Drives ComboBox, the root folders on this drive are displayed in the Folders ListBox control with the following statements, which are executed from within the SelectedIndexChanged event handler of the Drives ListBox control. First, the code retrieves the names of the folders in the root folder with the GetDirectories method and adds them to the FoldersList control. Then it retrieves the files of the root folder with the GetFiles method and displays their names on the FilesList control. Both FoldersList and FilesList are ListBox controls. The code shown in Listing 18.4 is executed when the user selects a drive in the ComboBox control.
Listing 18.4: Displaying a Drive’s Root Folders
Private Sub DrivesList_SelectedIndexChanged(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles DrivesList.SelectedIndexChanged
Dim directories() As String Try
directories = System.IO.Directory.GetDirectories(DrivesList.Text) Catch drvException As Exception
MsgBox(drvException.Message) Exit Sub
End Try
Dim dir As String FoldersList.Items.Clear() For Each dir In directories
FoldersList.Items.Add(dir) Next
FilesList.Items.Clear() End Sub
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |
SCANNING FOLDERS RECURSIVELY 823
The error handler catches any runtime exceptions, displays the appropriate message, and aborts the execution of the application. An exception will be thrown if the user attempts to display the contents of a drive that’s not ready (a CD drive that’s empty, for example).
Finally, we must add some code to the DoubleClick event handler of the FoldersList ListBox control. This action signals the user’s intention to switch to one of the displayed folders and view its subfolders. Listing 18.5 shows the code behind the DoubleClick event handler.
Listing 18.5: Displaying the Selected Folder’s Subfolders and Files
Private Sub FoldersList_DoubleClick(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles FoldersList.DoubleClick Dim selDir As String
selDir = FoldersList.Text Dim dirs(), files() As String If selDir = “..” Then
dirs = Directory.GetDirectories(parentDir) files = Directory.GetFiles(parentDir)
Try
parentDir = Directory.GetParent(parentDir).FullName Catch exc As Exception
parentDir = Nothing End Try
Else
dirs = Directory.GetDirectories(selDir) files = Directory.GetFiles(selDir)
Try
parentDir = Directory.GetParent(selDir).FullName Catch exc As Exception
parentDir = Nothing End Try
End If
Dim dir As String FoldersList.Items.Clear()
If Not parentDir Is Nothing Then FoldersList.Items.Add(“..”)
End If
For Each dir In dirs FoldersList.Items.Add(dir)
Next End Sub
The code in this event handler displays the subfolders of the selected folder in the same ListBox control, replacing its current contents. The very first item added to the Folders ListBox control is the symbol for the parent directory, which moves you to the parent folder. The code examines the selected item and, if it’s “..”, it displays the subfolders of the parent folder. Otherwise, it displays the
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |
824 Chapter 18 RECURSIVE PROGRAMMING
subfolders of the selected folder. The parentDir variable is declared on the Form level, and it’s used for storing the parent folder every time the user switches to a subfolder. It’s used to navigate back to the parent folder when the user clicks the parent folder symbol.
The error handler is actually part of the application’s logic. The code always attempts to retrieve the parent folder. If an error occurs, which means that the current folder has no parent folder, the parentDir variable is set to Nothing.
When the button on the form is clicked, the program starts scanning the selected folder recur- sively—it scans the files of the selected folder and then the files of all folders under the selected one. As it proceeds, it displays the name of the selected folder and the number of files and subfolders scanned so far on the form’s title bar. It also displays the path name of the folder being scanned on a Label control, above the FilesList control. Listing 18.6 is the code behind the Scan Selected Folder button, which initiates the recursive scanning.
Listing 18.6: Initiating the Recursive Scanning of a Folder
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click FilesList.Items.Clear()
If Button1.Text = “Scan Selected Folder” Then Button1.Text = “Stop Scanning”
interrupt = False Else
Button1.Text = “Scan Selected Folder” interrupt = True
Me.Text = “INTERRUPTED” End If
Try
If FoldersList.Text = “” Then Scanfolder(DrivesList.Text)
Else Scanfolder(FoldersList.Text)
End If
Catch scanException As Exception MsgBox(scanException.Message)
End Try
Button1.Text = “Scan Selected Folder” End Sub
First, this code clears the contents of the ListBox, where the files will be displayed. Then it changes the caption of the button to Stop Scanning, so that users will have a chance to interrupt the scan of an entire volume. This code demonstrates a technique for interrupting a recursive process. We set the interrupt variable to False when the scanning of the folder starts. If the user clicks the button while the scan is in progress, the interrupt variable is set to True. This variable is examined from within the recursive procedure, and you’ll see shortly how the program uses it to interrupt the process.
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |
SCANNING FOLDERS RECURSIVELY 825
The last If…End If clause initiates the scanning of the selected folder. If a folder name was selected in the FoldersList control, this is the folder that’s scanned recursively. If not, it attempts to scan recursively the selected drive. The appropriate error handler will display a message, should the user attempt to scan a drive that’s not ready or a folder that can’t be scanned successfully.
The recursive part of the application is the ScanFolder() subroutine, which is shown in Listing 18.7.
Listing 18.7: The ScanFolder() Subroutine
Sub ScanFolder(ByVal currDir As String) If interrupt Then Exit Sub
Dim Dir As String
Dim File As String
Dim FI As FileInfo
Label1.Text = “scanning “ & currDir
For Each File In Directory.GetFiles(currDir) FI = New FileInfo(File)
FilesList.Items.Add(FI.Name & vbTab & FI.Length & vbTab & _ FI.CreationTime)
Next
countFiles += Directory.GetFiles(currDir).Length
Me.Text = “Scanning “ & FoldersList.Text & “ — Processed “ & countFiles & _ “ files in “ & countFolders & “ folders...”
For Each Dir In Directory.GetDirectories(currDir) countFolders += 1
Application.DoEvents()
ScanFolder(Dir) Next
End Sub
The ScanFolder() subroutine examines the value of the interrupt variable. If it’s True, it terminates. Because ScanFolder() is recursive, all pending instances of the subroutine must terminate. As soon as the current instance terminates, the most recently interrupted instance of the subroutine kicks in and completes its execution. Then the next instance kicks in, until all pending instances of the subroutine have terminated. Depending on the complexity of the calculations performed by the recursive procedure, it may take a few seconds until the process is terminated.
The code that fills the FilesFolder ListBox control is straightforward: First, the subroutine adds the names of the files in the folder passed as argument (this is the selected folder on the FoldersList control). In addition to the name, it displays the file’s size and its creation date. After scanning all the files in the current folder, the program goes through each subfolder of the selected folder. Each one of these folders must also be scanned in the same manner, so the subroutine calls itself, passing the name of a different folder each time. This process is repeated for all the subfolders of the selected folder, at any depth.
The ScanFolder application is an interesting example of recursive programming, but why duplicate functionality that’s already available for free? One reason is that the FileScan application is highly customizable. In the previous section, you learned how to count all the files of a given folder,
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |
826 Chapter 18 RECURSIVE PROGRAMMING
including those in its subfolders. You can add many more useful features to the FileScan application that aren’t available through Windows Explorer. For example, you can implement a version of the Find utility that locates files and/or folders based on criteria that aren’t available through the Find utility. A limitation of the Find utility is that you can’t specify exclusion criteria. For instance, you can’t ask it to find all the files whose size exceeds 1 MB that aren’t system files (e.g., EXE, DLL) or images (e.g., BMP, TIF, JPG). But with FileScan, you can modify the application to handle all types of file selection or rejection criteria by designing the proper user interface. In the following section, we’ll modify the FileScan project a little, so that it maps a folder on a RichTextBox control.
VB.NET at Work: The FolderMap Project
Here’s another customization idea for the FileScan application: Have you ever had to prepare a hard copy of your hard disk’s structure? (If you ever have to submit the contents and structure of an entire CD to a publisher, this utility will save you a good deal of work.) As far as I know, there is no simple way to do it. However, you can easily modify the FileScan application so that it prints the contents of a folder, including its subfolders, to a text box. Figure 18.6 shows the FolderMap application, which does exactly that. The structure of a user-specified folder is printed on a RichTextBox control so that folder names can be displayed in bold and stand out. The contents of the text box can be copied and pasted in any other document or used in a mail message. In this example, I’m using the RichTextBox control to format the folder and filenames differently. The FolderMap project is also an interesting demonstration of creating formatted text from within your application with the help of the RichTextBox control.
Figure 18.6
The FolderMap application generates a text file with the structure of any given folder.
The code behind the Map Selected Folder button is identical to the code of the Scan Now button of the FileScan project, and it’s shown in Listing 18.8. It calls the ScanFolder() subroutine once, passing the name of the folder to be mapped.
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |
SCANNING FOLDERS RECURSIVELY 827
Listing 18.8: The Map Selected Folder Button
Private Sub bttnMapFolder_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles bttnMapFolder.Click RichTextBox1.Clear()
Me.Cursor = System.Windows.Forms.Cursors.WaitCursor countFiles = 0
countFolders = 1 Try
ScanFolder(FoldersList.SelectedItem) Catch scanException As Exception
MsgBox(scanException.Message) End Try
Me.Cursor = System.Windows.Forms.Cursors.Default End Sub
This code isn’t new to you. Let’s examine the code of the ScanFolder() subroutine, which is shown in Listing 18.9. It’s based on the ScanFolder() subroutine of the previous example, with the exception of the lines that format and display the filenames on the RichTextBox control at the bottom of the form. The subroutine prints the name of the current folder in bold. Then, it goes through the files in the current folder first and prints them on the RichTextBox control. All filenames are indented by a few spaces to the right, and they’re printed in regular font. The last For Each…Next loop in the subroutine goes through the subfolders of the current folder and calls the ScanFolder() subroutine for each one.
Listing 18.9: The Revised ScanFolder() Subroutine
Sub ScanFolder(ByVal currDir As String) Dim Dir As String
Dim File As String RichTextBox1.SelectionFont = boldFont RichTextBox1.AppendText(currDir & vbCrLf)
For Each File In System.IO.Directory.GetFiles(currDir) RichTextBox1.SelectionFont = textFont RichTextBox1.AppendText(“ “ & File & vbCrLf)
Next
countFiles += System.IO.Directory.GetFiles(currDir).Length
Me.Text = “scanned “ & countFiles & “ files in “ & countFolders & _ “ folders...”
For Each Dir In System.IO.Directory.GetDirectories(currDir) countFolders += 1
Application.DoEvents()
ScanFolder(Dir) Next
End Sub
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |
828 Chapter 18 RECURSIVE PROGRAMMING
At the beginning of the program, the variables boldFont and textFont are declared as follows:
Dim boldFont As New Font(“Verdana”, 11, System.Drawing.FontStyle.Bold)
Dim textFont As New Font(“Verdana”, 9, System.Drawing.FontStyle.Regular)
The code switches the RichTextBox control to bold font for printing folder names and back to regular font for printing filenames. The actual folder and filenames are appended to the existing contents with the control’s AppendText method.
While the application scans the selected folder, the pointer’s icon is switched to an hourglass, indicating that the process will take a while. Depending on the total number of files under the folder you’re mapping, the program may take a while to complete. Normally, you will create a printout of a folder with a few dozen, or even a few hundred, files. If you attempt to map the Program Files folder after the installation of Visual Studio, you will have to wait for a few minutes. Scanning the folders isn’t a slow process, but as the number of text lines in the RichTextBox increases, it takes more and more time to add new lines to the control.
An alternative is to append the filenames to a disk file rather than a memory variable, then open the file and read its contents into the RichTextBox control. Or, if you don’t care about displaying folder names in bold, you could abandon the RichTextBox control and use a TextBox or a ListBox control. You could also use a StringBuilder variable to store all the folder and filenames in a really long variable and then display this string on the TextBox control. I think the benefit of richly formatting the folder structure offsets the less-than-optimal execution speed, as long as the size of the folder you want to map is reasonable.
Further Customization
Another customization idea is to process selected files with a specific application. Suppose your DownLoad folder is full of ZIP files you have downloaded from various sources. Unzipping these files into the DownLoad folder would be a disaster. Ideally, you should create a separate folder for each ZIP file, copy a single ZIP file there, and then unzip it. You can do this manually or you can let a variation of the FileScan application do it for you. All you need is a small program that creates the folder, moves the ZIP file there, and then unzips it with PK_UNZIP (of course, any zipping/unzipping utility will work in a similar manner.) You could even write a DOS batch file to process the ZIP files with the following statements:
md c:\Shareware\%2
copy %1 c:\Shareware\%2\ del %1
pkunzip c:\Shareware\%1
Tip A batch file is a program that can be started with the Shell function. To start the PK_UNZIP application from within Visual Basic, use a statement like Shell(“pkunzip c:\zipfiles\*.ZIP”).
If this batch file is named MVFiles.bat, you can call it with two arguments:
MVFILES CuteUtility.zip CuteUtility
The first argument is the name of the ZIP file to be moved and unzipped, and the second argument is the name of the folder where the ZIP file will be moved and unzipped. You can modify the FileScan
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |