In this chapter, I will continue the discussion of the Windows controls. I'll start with the controls that implement the common dialog boxes and the RichTextBox control. Then I will deal with two more advanced controls, TreeView and ListView.
The .NET Framework provides a set of controls for displaying common dialog boxes, such as the Open and Color dialog boxes. Each of these controls encapsulates a large amount of functionality that would take a lot of code to duplicate. The common dialog controls are fundamental components because they enable you to design user interfaces with the look and feel of a Windows application.
You'll also explore the RichTextBox control, which is an advanced version of the TextBox control. The RichTextBox control provides all the functionality you'll need to build a word processor — WordPad is actually built around the RichTextBox control. The RichTextBox control allows you to format text by mixing fonts and attributes, aligning paragraphs differently, and so on.
The TreeView and ListView controls implement two of the more-advanced data structures. TreeView can be used to present a hierarchical list — a tree in which items that belong to other items appear under their parent with the proper indentation. For instance, a list of city and state names should be structured so that each city appears under the corresponding state. ListView can be used to present a "flat" structure where each item has a number of subitems. A typical example is a file, whose most important attributes are name, size, type, and modification date. These attributes can be presented as subitems in a list of files.
The TreeView and ListView controls were designed to hide much of the complexity of these structures, and they do this very well. They are among the more-advanced controls, and they are certainly more difficult to program than the ones discussed in the preceding chapters. These two controls, however, are the basic makings of unique user interfaces, as you'll see in this chapter's examples.
In this chapter you'll learn how to do the following:
Use the OpenFileDialog and SaveFileDialog controls to prompt users for filenames.
Use ColorDialog and FontDialog controls to prompt users for colors and typefaces.
Use the RichTextBox control as an advanced text editor to present richly formatted text.
Use the TreeView and ListView controls to present hierarchical lists and lists of structured items.
A rather tedious, but quite common, task in nearly every application is to prompt the user for filenames, font names and sizes, or colors to be used by the application. Designing your own dialog boxes for these purposes would be a hassle, not to mention that your applications wouldn't conform to the basic Windows interface design principles. Truth be told, users are not fond of surprises, and all your creative effort will most likely backfire. Unexpected interface features are guaranteed to curb GUI usability and result in a number of frustrated users. In fact, all Windows applications use standard dialog boxes for common operations; two of them are shown in Figure 7.1. These dialog boxes are implemented as standard controls in the Toolbox. To use any of the common dialog controls in your interface, just place the appropriate control from the Dialogs section of the Toolbox on your form and activate it from within your code by calling the ShowDialog
method.
The common dialog controls are invisible at runtime, and they're not placed on your forms because they're implemented as modal dialog boxes and they're displayed as needed. You simply add them to the project by double-clicking their icons in the Toolbox; a new icon appears in the components tray of the form, just below the Form Designer. The following common dialog controls are in the Toolbox under the Dialogs tab:
Lets users select a file to open. It also allows the selection of multiple files for applications that must process many files at once.
Lets users select or specify the path of a file in which the current document will be saved.
Lets users select a folder (an operation that can't be performed with the OpenFileDialog control).
Lets users select a color from a list of predefined colors or specify custom colors.
Lets users select a typeface and style to be applied to the current text selection. The Font dialog box has an Apply button, which you can intercept from within your code and use to apply the currently selected font to the text without closing the dialog box.
There are three more common dialog controls: the PrintDialog, PrintPreviewDialog, and PageSetupDialog controls. These controls are discussed in detail in the tutorial "Printing with Visual Basic 2010," available for download from www.sybex.com/go/masteringvb2010
, in the context of VB's printing capabilities.
To display any of the common dialog boxes from within your application, you must first add an instance of the appropriate control to your project. Then you must set some basic properties of the control through the Properties window. Most applications set the control's properties from within the code because common dialogs interact closely with the application. When you call the Color common dialog, for example, you should preselect a color from within your application and make it the default selection on the control. When prompting the user for the color of the text, the default selection should be the current setting of the control's ForeColor
property. Likewise, the Save dialog box must suggest a filename when it first pops up (or the filename's extension, at least).
To display a common dialog box from within your code, you simply call the control's ShowDialog
method, which is common for all controls. Note that all common dialog controls can be displayed only modally and they don't expose a Show
method. As soon as you call the ShowDialog
method, the corresponding dialog box appears onscreen, and the execution of the program is suspended until the box is closed. Using the Open, Save, and FolderBrowser dialog boxes, users can traverse the entire structure of their drives and locate the desired filename or folder. When the user clicks the Open or Save button, the dialog box closes and the program's execution resumes. The code should read the name of the file selected by the user through the FileName
property and use it to open the file or store the current document there. The folder selected in the FolderBrowserDialog control is returned to the application through the SelectedPath
property.
Here is the sequence of statements used to invoke the Open common dialog and retrieve the selected filename:
If OpenFileDialog1.ShowDialog = Windows.Forms.DialogResult.OK Then fileName = OpenFileDialog1.FileName ' Statements to open the selected file End If
The ShowDialog
method returns a value indicating how the dialog box was closed. You should read this value from within your code and ignore the settings of the dialog box if the operation was cancelled.
The variable fileName
in the preceding code segment is the full pathname of the file selected by the user. You can also set the FileName
property to a filename, which will be displayed when the Open dialog box is first opened:
OpenFileDialog1.FileName = "C:WorkFilesDocumentsDocument1.doc" If OpenFileDialog1.ShowDialog = Windows.Forms.DialogResult.OK Then fileName = OpenFileDialog1.FileName ' Statements to open the selected file End If
Similarly, you can invoke the Color dialog box and read the value of the selected color by using the following statements:
ColorDialog1.Color = TextBox1.BackColor If ColorDialog1.ShowDialog = DialogResult.OK Then TextBox1.BackColor = ColorDialog1.Color End If
The ShowDialog
method is common to all controls. The Title
property is also common to all controls and it's the string displayed in the title bar of the dialog box. The default title is the name of the dialog box (for example, Open, Color, and so on), but you can adjust it from within your code with a statement such as the following:
ColorDialog1.Title = "Select Drawing Color"
The Color dialog box, shown in Figure 7.2, is one of the simplest dialog boxes. Its Color
property returns the color selected by the user or sets the initially selected color when the user opens the dialog box.
The following statements set the initial color of the ColorDialog control, display the dialog box, and then use the color selected in the control to fill the form. First, place a ColorDialog control in the form and then insert the following statements in a button's Click
event handler:
Private Sub Button1_Click(...) Handles Button1.Click ColorDialog1.Color = Me.BackColor If ColorDialog1.ShowDialog = Windows.Forms.DialogResult.OK Then Me.BackColor = ColorDialog1.Color End If End Sub
The following sections discuss the basic properties of the ColorDialog control.
Set this property to True if you want users to be able to open the dialog box and define their own custom colors, as you can in the one shown in Figure 7.2. The AllowFullOpen
property doesn't open the custom section of the dialog box; it simply enables the Define Custom Colors button in the dialog box. Otherwise, this button is disabled.
This property is a Boolean value that determines whether the dialog box displays all available colors in the set of basic colors.
This is the color specified on the control. You can set it to a color value before showing the dialog box to suggest a reasonable selection. On return, read the value of the same property to find out which color was picked by the user in the control:
ColorDialog1.Color = Me.BackColor If ColorDialog1.ShowDialog = DialogResult.OK Then Me.BackColor = ColorDialog1.Color End If
This property indicates the set of custom colors that will be shown in the dialog box. The Color dialog box has a section called Custom Colors, in which you can display 16 additional custom colors. The CustomColors
property is an array of integers that represent colors. To display three custom colors in the lower section of the Color dialog box, use a statement such as the following:
Dim colors() As Integer = {222663, 35453, 7888} ColorDialog1.CustomColors = colors
You'd expect that the CustomColors
property would be an array of Color values, but it's not. You can't create the array CustomColors
with a statement such as this one:
Dim colors() As Color = {Color.Azure, Color.Navy, Color.Teal}
Because it's awkward to work with numeric values, you should convert color values to integer values by using a statement such as the following:
Color.Navy.ToArgb
The preceding statement returns an integer value that represents the color navy. This value, however, is negative because the first byte in the color value represents the transparency of the color. To get the value of the color, you must take the absolute value of the integer value returned by the previous expression. To create an array of integers that represent color values, use a statement such as the following:
Dim colors() As Integer = {Math.Abs(Color.Gray.ToArgb), Math.Abs(Color.Navy.ToArgb), Math.Abs(Color.Teal.ToArgb)}
Now you can assign the colors
array to the CustomColors
property of the control and the colors will appear in the Custom Colors section of the Color dialog box.
This indicates whether the dialog box will restrict users to selecting solid colors only. This setting should be used with systems that can display only 256 colors. Although today few systems can't display more than 256 colors, some interfaces are limited to this number. When you run an application through Remote Desktop, for example, only the solid colors are displayed correctly on the remote screen regardless of the remote computer's graphics card (and that's for efficiency reasons).
The Font dialog box, shown in Figure 7.3, lets the user review and select a font and then set its size and style. Optionally, by clicking the Apply button users can also select the font's color and even apply the current settings to the selected text on a control of the form without closing the dialog box. This button isn't displayed by default; to show this button, you must set the control's ShowApply
property to True. To see how the Apply button is used, see the description of the ShowApply property a little later in this section.
When the dialog is closed by clicking the OK button, you can retrieve the selected font by using the control's Font
property. In addition to the OK button, the Font dialog box may contain the Apply button, which reports the current setting to your application. You can intercept the Click
event of the Apply button and adjust the appearance of the text on your form while the common dialog is still visible.
The main property of this control is the Font
property, which sets the initially selected font in the dialog box and retrieves the font selected by the user. The following statements display the Font dialog box after setting the initial font to the current font of the TextBox1
control. When the user closes the dialog box, the code retrieves the selected font and assigns it to the same TextBox control:
FontDialog1.Font = TextBox1.Font If FontDialog1.ShowDialog = DialogResult.OK Then TextBox1.Font = FontDialog1.Font End If
Use the following properties to customize the Font dialog box before displaying it.
This property is a Boolean value that indicates whether the Script combo box will be displayed in the Font dialog box. This combo box allows the user to change the current character set and select a non-Western language (such as Greek, Hebrew, Cyrillic, and so on).
This property is a Boolean value that indicates whether the dialog box allows the display and selection of both vertical and horizontal fonts. Its default value is False, which displays only horizontal fonts.
The Color
property sets or returns the selected font color. To enable users to select a color for the font, you must also set the ShowColor
property to True.
This property is a Boolean value that indicates whether the dialog box allows only the selection of fixed-pitch fonts. Its default value is False, which means that all fonts (fixed- and variable-pitch fonts) are displayed in the Font dialog box. Fixed-pitch fonts, or monospaced fonts, consist of characters of equal widths that are sometimes used to display columns of numeric values so that the digits are aligned vertically.
This property is a Font object. You can set it to the preselected font before displaying the dialog box and assign it to a Font
property upon return. You've already seen how to preselect a font and how to apply the selected font to a control from within your application.
You can also create a new Font object and assign it to the control's Font
property. Upon return, the TextBox control's Font
property is set to the selected font:
Dim newFont As New Font("Verdana", 12, FontStyle.Underline) FontDialog1.Font = newFont If FontDialog1.ShowDialog() = DialogResult.OK Then TextBox1.ForeColor = FontDialog1.Color End If
This property is a Boolean value that indicates whether the dialog box forces the selection of an existing font. If the user enters a font name that doesn't correspond to a name in the list of available fonts, a warning is displayed. Its default value is True, and there's no reason to change it.
These two properties are integers that determine the minimum and maximum point size the user can specify in the Font dialog box. Use these two properties to prevent the selection of extremely large or extremely small font sizes because these fonts might throw off a well-balanced interface (text will overflow in labels, for example).
This property is a Boolean value that indicates whether the dialog box provides an Apply button. Its default value is False, so the Apply button isn't normally displayed. If you set this property to True, you must also program the control's Apply
event — the changes aren't applied automatically to any of the controls in the current form.
The following statements display the Font dialog box with the Apply button:
Private Sub Button2_Click(...) Handles Button2.Click FontDialog1.Font = TextBox1.Font FontDialog1.ShowApply = True If FontDialog1.ShowDialog = DialogResult.OK Then TextBox1.Font = FontDialog1.Font End If End Sub
The FontDialog control raises the Apply
event every time the user clicks the Apply button. In this event's handler, you must read the currently selected font and use it in the form so that users can preview the effect of their selection:
Private Sub FontDialog1_Apply(...) Handles FontDialog1.Apply TextBox1.Font = FontDialog1.Font End Sub
This property is a Boolean value that indicates whether the dialog box allows the selection of special text effects, such as strikethrough and underline. The effects are returned to the application as attributes of the selected Font object, and you don't have to do anything special in your application.
Open and Save As, the two most widely used common dialog boxes (see Figure 7.4), are implemented by the OpenFileDialog and SaveFileDialog controls. Nearly every application prompts users for filenames, and the .NET Framework provides two controls for this purpose. The two dialog boxes are nearly identical, and most of their properties are common, so we'll start with the properties that are common to both controls.
When either of the two controls is displayed, it rarely displays all the files in any given folder. Usually the files displayed are limited to the ones that the application recognizes so that users can easily spot the file they want. The Filter
property limits the types of files that will appear in the Open or Save As dialog box.
It's also standard for the Windows interface not to display the extensions of filenames (although Windows distinguishes files by their filename extensions). The file type ComboBox, which appears at the bottom of the form next to the File Name box, contains the various file types recognized by the application. The various file types can be described in plain English with long descriptive names and without their filename extensions.
The extension of the default file type for the application is described by the DefaultExtension
property, and the list of the file types displayed in the Save As Type box is determined by the Filter
property.
To prompt the user for a file to be opened, use the following statements. The Open dialog box displays the files with the filename extension .bin
only:
OpenFileDialog1.DefaultExt = ".bin" OpenFileDialog1.AddExtension = True OpenFileDialog1.Filter = "Binary Files|*.bin" If OpenFileDialog1.ShowDialog() = Windows.Forms.DialogResult.OK Then Debug.WriteLine(OpenFileDialog1.FileName) End If
The following sections describe the properties of the OpenFileDialog and SaveFileDialog controls.
This property is a Boolean value that determines whether the dialog box automatically adds an extension to a filename if the user omits it. The extension added automatically is the one specified by the DefaultExtension
property, which you must set before calling the ShowDialog
method. This is the default filename extension of the files recognized by your application.
This property is a Boolean value that indicates whether the dialog box displays a warning if the user enters the name of a file that does not exist in the Open dialog box or if the user enters the name of a file that exists in the Save dialog box.
This property is a Boolean value that indicates whether the dialog box displays a warning if the user specifies a path that does not exist as part of the user-supplied filename.
This property sets the default extension for the filenames specified on the control. Use this property to specify a default filename extension, such as .txt
or .doc
, so that when a file with no extension is specified by the user, the default extension is automatically appended to the filename. You must also set the AddExtension
property to True. The default extension property starts with the period, and it's a string — for example, .bin
.
This property indicates whether the dialog box returns the location of the file referenced by the shortcut or the location of the shortcut itself. If you attempt to select a shortcut on your Desktop when the DereferenceLinks
property is set to False, the dialog box will return to your application a value such as C:WINDOWSSYSTEM32lnkstub.exe
, which is the name of the shortcut, not the name of the file represented by the shortcut. If you set the DereferenceLinks
property to True, the dialog box will return the actual filename represented by the shortcut, which you can use in your code.
Use this property to retrieve the full path of the file selected by the user in the control. If you set this property to a filename before opening the dialog box, this value will be the proposed filename. The user can click OK to select this file or select another one in the control. The two controls provide another related property, the FileNames
property, which returns an array of filenames. To find out how to allow the user to select multiple files, see the discussion of the MultiSelect
and FileNames
properties later in this chapter.
This property is used to specify the type(s) of files displayed in the dialog box. To display text files only, set the Filter
property to Text files|*.txt
. The pipe symbol separates the description of the files (what the user sees) from the actual filename extension (how the operating system distinguishes the various file types).
If you want to display multiple extensions, such as .bmp, .gif
, and .jpg
, use a semicolon to separate extensions with the Filter
property. Set the Filter property to the string Images|*.bmp;*.gif;*.jpg
to display all the files of these three types when the user selects Images in the Save As Type combo box under the box with the filename.
Don't include spaces before or after the pipe symbol because these spaces will be displayed on the dialog box. In the Open dialog box of an image-processing application, you'll probably provide options for each image file type as well as an option for all images:
OpenFileDialog1.Filter = "Bitmaps|*.bmp|GIF Images|*.gif|" & "JPEG Images|*.jpg|All Images|*.bmp;*.gif;*.png"
When you specify more than one file type when using the Filter
property of the Open dialog box, the first file type becomes the default. If you want to use a file type other than the first one, use the FilterIndex
property to determine which file type will be displayed as the default when the Open dialog box is opened. The index of the first type is 1, and there's no reason to ever set this property to 1. If you use the Filter
property value of the example in the preceding section and set the FilterIndex
property to 2, the Open dialog box will display GIF files by default.
This property sets the initial folder whose files are displayed the first time that the Open and Save dialog boxes are opened. Use this property to display the files of the application's folder or to specify a folder in which the application stores its files by default. If you don't specify an initial folder, the dialog box will default to the last folder where the most recent file was opened or saved. It's also customary to set the initial folder to the application's path by using the following statement:
OpenFileDialog1.InitialDirectory = Application.ExecutablePath
The expression Application.ExecutablePath
returns the path in which the application's executable file resides.
Every time the Open and Save As dialog boxes are displayed, the current folder is the one that was selected by the user the last time the control was displayed. The RestoreDirectory
property is a Boolean value that indicates whether the dialog box restores the current directory before closing. Its default value is False, which means that the initial directory is not restored automatically. The InitialDirectory
property overrides the RestoreDirectory
property.
If the Open dialog box allows the selection of multiple files (you have set the MultiSelect
property to True), the FileNames
property contains the pathnames of all selected files. FileNames
is a collection, and you can iterate through the filenames with an enumerator. This property should be used only with the OpenFileDialog control, even though the SaveFileDialog control exposes a FileNames
property.
This property is a Boolean value that indicates whether the user can select multiple files in the dialog box. Its default value is False, and users can select a single file. When the MultiSelect
property is True, the user can select multiple files, but they must all come from the same folder (you can't allow the selection of multiple files from different folders). This property is unique to the OpenFileDialog control. This and the following two properties are unique to the OpenFileDialog control.
The ReadOnlyChecked
property is a Boolean value that indicates whether the Read-Only check box is selected when the dialog box first pops up (the user can clear this box to open a file in read/write mode). You can set this property to True only if the ShowReadOnly
property is also set to True. The ShowReadOnly
property is also a Boolean value that indicates whether the Read-Only check box is available. If this check box appears on the form, the user can select it so the file will be opened as read-only. Files opened as read-only shouldn't be saved with the same filename — always prompt the user for a new filename.
The OpenFileDialog control exposes the OpenFile
method, which allows you to quickly open the selected file. Likewise, the SaveFileDialog control exposes the SaveFile
method, which allows you to quickly save a document to the selected file. Normally, after retrieving the name of the file selected by the user, you must open this file for reading (in the case of the Open dialog box) or writing (in the case of the Save dialog box). The topic of reading from or writing to files is discussed in detail in the tutorial "Accessing Files and Folders with the System.IO Class," which is available for download at www.sybex.com/go/masteringvb2010
.
When this method is applied to the Open dialog box, the file is opened with read-only permission. The same method can be applied to the SaveFile dialog box, in which case the file is opened with read-write permission. Both methods return a Stream object, and you can call this object's Read
and Write
methods to read from or write to the file.
The Open dialog box allows the selection of multiple files. This feature can come in handy when you want to process files en masse. You can let the user select many files, usually of the same type, and then process them one at a time. Or, you might want to prompt the user to select multiple files to be moved or copied.
To allow the user to select multiple files in the Open dialog box, set the MultiSelect
property to True. The user can then select multiple files with the mouse by holding down the Shift or Ctrl key. The names of the selected files are reported by the property FileNames
, which is an array of strings. The FileNames
array contains the pathnames of all selected files, and you can iterate through them and process each file individually.
One of this chapter's sample projects is the MultipleFiles project, which demonstrates how to use the FileNames
property. The application's form is shown in Figure 7.5. The button at the top of the form, Show Files in Folder, displays the Open dialog box, where you can select multiple files. After closing the dialog box by clicking the Open button on the Open dialog box, the application displays the pathnames of the selected files on a ListBox control.
The code behind the Open Files button is shown in Listing 7.1. In this example, I used the array's enumerator to iterate through the elements of the FileNames
array. You can use any of the methods discussed in Chapter 2, "VB Programming Essentials" to iterate through the array.
Example 7.1. Processing multiple selected files
Private Sub bttnFile_Click(...) Handles bttnFile.Click OpenFileDialog1.Multiselect = True OpenFileDialog1.ShowDialog() Dim filesEnum As IEnumerator ListBox1.Items.Clear() filesEnum = OpenFileDialog1.FileNames.GetEnumerator() While filesEnum.MoveNext ListBox1.Items.Add(filesEnum.Current) End While End Sub
Sometimes we need to prompt users for a folder rather than a filename. An application that processes files in batch mode shouldn't force users to select the files to be processed. Instead, it should allow users to select a folder and process all files of a specific type in the folder (it could encrypt all text documents or resize all image files, for example). As elaborate as the File Open dialog box might be, it doesn't allow the selection of a folder. To prompt users for a folder's path, use the FolderBrowser dialog box, which is a very simple one; it's shown in Figure 7.6. The FolderBrowserDialog control exposes a small number of properties, which are discussed next.
This property indicates the initial folder to be displayed when the dialog box is shown. It is not necessarily a string; it can also be a member of the SpecialFolder
enumeration. To see the members of the enumeration, enter the following expression:
FolderBrowserDialog1.RootFolder =
As soon as you enter the equals sign, you will see the members of the enumeration. The most common setting for this property is My Computer, which represents the target computer's file system. You can set the RootFolder
property to a number of special folders (for example, Personal, Desktop, ApplicationData, LocalApplicationData, and so on). You can also set this property to a string with the desired folder's pathname.
After the user closes the FolderBrowser dialog box by clicking the OK button, you can retrieve the name of the selected folder with the SelectedPath
property, which is a string, and you can use it with the methods of the System.IO
namespace to access and manipulate the selected folder's files and subfolders.
This property determines whether the dialog box will contain a New button; its default value is True. When users click the New button to create a new folder, the dialog box prompts them for the new folder's name and creates a new folder with the specified name under the selected folder.
The FolderBrowser control is a trivial control, but I'm including a sample application, available for download from www.sybex.com/go/masteringvb2010
, to demonstrate its use. The same application demonstrates how to retrieve the files and subfolders of the selected folder and how to create a directory listing in a RichTextBox control, like the one shown in Figure 7.6. The members of the System.IO
namespace, which allow you to access and manipulate files and folders from within your code, are discussed in detail in the tutorial "Accessing Files and Folders," which is available for download at www.sybex.com/go/masteringvb2010
.
The FolderBrowser dialog box is set to display the entire file system of the target computer and is invoked with the following statements:
FolderBrowserDialog1.RootFolder = Environment.SpecialFolder.MyComputer FolderBrowserDialog1.ShowNewFolderButton = False If FolderBrowserDialog1.ShowDialog = DialogResult.OK Then ' process files in selected folder End If
As usual, we examine the value returned by the ShowDialog
method of the control and we proceed if the user has closed the dialog box by clicking the OK button. The code that iterates through the selected folder's files and subfolders, shown in Listing 7.2, is basically a demonstration of some members of the System.IO
namespace, but I'll review it briefly here.
Example 7.2. Scanning a folder
Private Sub bttnSelectFiles_Click(...) Handles bttnSelectFiles.Click FolderBrowserDialog1.RootFolder = Environment.SpecialFolder.MyComputer FolderBrowserDialog1.ShowNewFolderButton = False If FolderBrowserDialog1.ShowDialog = Windows.Forms.DialogResult.OK Then RichTextBox1.Clear() ' Retrieve initial folder Dim initialFolder As String = FolderBrowserDialog1.SelectedPath Dim InitialDir As New IO.DirectoryInfo( FolderBrowserDialog1.SelectedPath) ' and print its name w/o any indentation PrintFolderName(InitialDir, "") ' and then print the files in the top folder If InitialDir.GetFiles("*.*").Length = 0 Then SwitchToItalics() RichTextBox1.AppendText( "folder contains no files" & vbCrLf) SwitchToRegular() Else PrintFileNames(InitialDir, "") End If Dim DI As IO.DirectoryInfo ' Iterate through every subfolder and print it For Each DI In InitialDir.GetDirectories PrintDirectory(DI) Next End If End Sub
The selected folder's name is stored in the initialFolder
variable and is passed as an argument to the constructor of the DirectoryInfo class. The InitialDir
variable represents the specified folder. This object is passed to the PrintFolderName()
subroutine, which prints the folder's name in bold. Then the code iterates through the same folder's files and prints them with the PrintFileNames()
subroutine, which accepts as an argument the DirectoryInfo object that represents the current folder and the indentation level. After printing the initial folder's name and the names of the files in the folder, the code iterates through the subfolders of the initial folder. The GetDirectories
method of the DirectoryInfo class returns a collection of objects, one for each subfolder under the folder represented by the InitialDir
variable. For each subfolder, it calls the PrintDirectory()
subroutine, which prints the folder's name and the files in this folder, and then iterates through the folder's subfolders. The code that iterates through the selected folder's files and subfolders is shown in Listing 7.3.
Example 7.3. The PrintDirectory()
subroutine
Private Sub PrintDirectory(ByVal CurrentDir As IO.DirectoryInfo) Static IndentationLevel As Integer = 0 IndentationLevel += 1 Dim indentationString As String = "" indentationString = New String(Convert.ToChar(vbTab), IndentationLevel) PrintFolderName(CurrentDir, indentationString) If CurrentDir.GetFiles("*.*").Length = 0 Then SwitchToItalics() RichTextBox1.AppendText(indentationString & "folder contains no files" & vbCrLf) SwitchToRegular() Else PrintFileNames(CurrentDir, indentationString) End If Dim folder As IO.DirectoryInfo For Each folder In CurrentDir.GetDirectories PrintDirectory(folder) Next IndentationLevel -= 1 End Sub
The code that iterates through the subfolders of a given folder is discussed in detail in the tutorial "Accessing Files and Folders," available for download from www.sybex.com/go/masteringvb2010
, so you need not worry if you can't figure out how it works yet. In the following sections, you'll learn how to display formatted text in the RichTextBox control.
The RichTextBox control is the core of a full-blown word processor. It provides all the functionality of a TextBox control; it can handle multiple typefaces, sizes, and attributes and offers precise control over the margins of the text (see Figure 7.7). You can even place images in your text on a RichTextBox control (although you won't have the kind of control over the embedded images that you have with Microsoft Word).
The fundamental property of the RichTextBox control is its Rtf
property. Similar to the Text
property of the TextBox control, this property is the text displayed on the control. Unlike the Text
property, however, which returns (or sets) the text of the control but doesn't contain formatting information, the Rtf
property returns the text along with any formatting information. Therefore, you can use the RichTextBox control to specify the text's formatting, including paragraph indentation, font, and font size or style.
RTF, which stands for Rich Text format, is a standard for storing formatting information along with the text. The beauty of the RichTextBox control for programmers is that they don't need to supply the formatting codes. The control provides simple properties to change the font of the selected text, change the alignment of the current paragraph, and so on. The RTF code is generated internally by the control and used to save and load formatted files. It's possible to create elaborately formatted documents without knowing the RTF specification.
The WordPad application that comes with Windows is based on the RichTextBox control. You can easily duplicate every bit of WordPad's functionality with the RichTextBox control, as you will see later in this chapter.
A basic knowledge of the Rich Text format, its commands, and how it works will certainly help you understand the RichTextBox control's inner workings. RTF is a language that uses simple commands to specify the formatting of a document. These commands, or tags, are ASCII strings, such as par
(the tag that marks the beginning of a new paragraph) and (the tag that turns on the bold style). And this is where the value of the Rich Text format lies. RTF documents don't contain special characters and can be easily exchanged among different operating systems and computers, as long as there is an RTF-capable application to read the document.
RTF is similar to Hypertext Markup Language (HTML), and if you're familiar with HTML, a few comparisons between the two standards will provide helpful hints and insight into the RTF language. Like HTML, RTF was designed to create formatted documents that could be displayed on different systems. The following RTF segment displays a sentence with a few words in italic:
RTF0 (which stands for Rich Text Format) is a i document formatting languagei0 that uses simple commands to specify the formatting of the document.
The following is the equivalent HTML code:
<b>RTF</b> (which stands for Rich Text Format) is a <i>document formatting language</i> that uses simple commands to specify the formatting of the document.
The <b>
and <i>
tags of HTML, for example, are equivalent to the and
i
tags of RTF. The closing tags in RTF are 0
and i0
, respectively.
Although you don't need to understand the RTF specifications to produce formatted text with the RichTextBox control, if you want to generate RTF documents from within your code, visit the RTF Cookbook site at http://search.cpan.org/∼sburke/RTF-Writer/lib/RTF/Cookbook.pod
. There's also a Microsoft resource on RTF at http://msdn2.microsoft.com/en-us/library/aa140277(office.10).aspx
.
The RichTextBox control provides properties for manipulating the selected text on the control. The names of these properties start with the Selection
or Selected
prefix, and the most commonly used ones are shown in Table 7.1. Some of these properties are discussed in further detail in following sections.
Table 7.1. RichTextBox properties for manipulating selected text
Property | What It Manipulates |
---|---|
| The selected text |
| The RTF code of the selected text |
| The position of the selected text's first character |
| The length of the selected text |
| The font of the selected text |
| The color of the selected text |
| The background color of the selected text |
| The alignment of the selected text |
| The indentation of the selected text |
| The distance of the text's right margin from the left edge of the control |
| An array of integers that sets the tab stop positions in the control |
| Whether the selected text is bulleted |
| The amount of bullet indent for the selected text |
The SelectedText
property represents the selected text, whether it was selected by the user via the mouse or from within your code. To assign the selected text to a variable, use the following statement:
selText=RichTextbox1.SelectedText
You can also modify the selected text by assigning a new value to the SelectedText
property. The following statement converts the selected text to uppercase:
RichTextbox1.SelectedText = RichTextbox1.SelectedText.ToUpper
You can assign any string to the SelectedText
property. If no text is selected at the time, the statement will insert the string at the location of the pointer.
To simplify the manipulation and formatting of the text on the control, two additional properties, SelectionStart
and SelectionLength
, report (or set) the position of the first selected character in the text and the length of the selection, respectively, regardless of the formatting of the selected text. One obvious use of these properties is to select (and highlight) some text on the control:
RichTextBox1.SelectionStart = 0 RichTextBox1.SelectionLength = 100
You can also use the Select
method, which accepts as arguments the starting location and the length of the text to be selected.
Use this property to read or change the alignment of one or more paragraphs. This property's value is one of the members of the HorizontalAlignment
enumeration: Left, Right, and Center. Users don't have to select an entire paragraph to align it; just placing the pointer anywhere in the paragraph will do the trick because you can't align part of the paragraph.
These properties allow you to change the margins of individual paragraphs. The SelectionIndent
property sets (or returns) the amount of the text's indentation from the left edge of the control. The SelectionRightIndent
property sets (or returns) the amount of the text's indentation from the right edge of the control. The SelectionHangingIndent
property indicates the indentation of each paragraph's first line with respect to the following lines of the same paragraph. All three properties are expressed in pixels.
The SelectionHangingIndent
property includes the current setting of the SelectionIndent
property. If all the lines of a paragraph are aligned to the left, the SelectionIndent
property can have any value (this is the distance of all lines from the left edge of the control), but the SelectionHangingIndent
property must be zero. If the first line of the paragraph is shorter than the following lines, the SelectionHangingIndent
has a negative value. Figure 7.8 shows several differently formatted paragraphs. The settings of the SelectionIndent
and SelectionHangingIndent
properties are determined by the two sliders at the top of the form.
You use these properties to create a list of bulleted items. If you set the SelectionBullet
property to True, the selected paragraphs are formatted with a bullet style, similar to the <ul>
tag in HTML. To create a list of bulleted items, select them from within your code and assign the value True to the SelectionBullet
property. To change a list of bulleted items back to normal text, make the same property False.
The paragraphs formatted as bullets are also indented from the left by a small amount. To set the amount of the indentation, use the BulletIndent
property, which is also expressed in pixels.
The first two methods of the RichTextBox control you need to know are SaveFile
and LoadFile
. The SaveFile
method saves the contents of the control to a disk file, and the LoadFile
method loads the control from a disk file.
The syntax of the SaveFile
method is as follows, where path
is the path of the file in which the current document will be saved:
RichTextBox1.SaveFile(path, filetype)
By default, the SaveFile
method saves the document in RTF format and uses the .rtf
extension. You can specify a different format by using the second optional argument, which can take on the value of one of the members of the RichTextBoxStreamType
enumeration, described in Table 7.2.
Similarly, the LoadFile
method loads a text or RTF file to the control. Its syntax is identical to the syntax of the SaveFile
method:
RichTextBox1.LoadFile(path, filetype)
Table 7.2. The RichTextBoxStreamType
enumeration
Effect | |
---|---|
| Stores the text on the control without any formatting |
| Stores the text without any formatting and ignores any embedded OLE (Object Linking and Embedding) objects |
| Stores the text in RTF format (text with embedded RTF commands) |
| Stores the text along with the embedded OLE objects |
| Stores the text in Unicode format |
The filetype
argument is optional and can have one of the values of the RichTextBoxStreamType
enumeration. Saving and loading files to and from disk files is as simple as presenting a Save or Open common dialog to the user and then calling one of the SaveFile
or LoadFile
methods with the filename returned by the common dialog box.
The Select
method selects a section of the text on the control, similar to setting the SelectionStart
and SelectionLength
properties. The Select
method accepts two arguments, the location of the first character to be selected and the length of the selection:
RichTextBox1.Select(start, length
)
The SelectAll
method accepts no arguments and it selects all the text on the control.
The RichTextBox control provides all the text-editing features you'd expect to find in a text-editing application, similar to the TextBox control. Among its more-advanced features, the RichTextBox control provides the AutoWordSelection
property, which controls how the control selects text. If it's True, the control selects a word at a time.
In addition to formatted text, the RichTextBox control can handle object linking and embedding (OLE) objects. You can insert images in the text by pasting them with the Paste
method. The Paste
method doesn't require any arguments; it simply inserts the contents of the Clipboard at the current location (the location of the cursor) in the document.
Unlike the plain TextBox control, the RichTextBox control encapsulates undo and redo operations at multiple levels. Each operation has a name (Typing, Deletion, and so on), and you can retrieve the name of the next operation to be undone or redone and display it on the menu. Instead of a simple Undo or Redo caption, you can change the captions of the Edit menu to something like Undo Delete or Redo Typing. To program undo and redo operations from within your code, you must use the properties and methods discussed in the following sections.
These two properties are Boolean values you can read to find out whether there's an operation that can be undone or redone. If they're False, you must disable the corresponding menu command from within your code. The following statements disable the Undo command if there's no action to be undone at the time (EditUndo
is the name of the Undo command on the Edit menu):
If RichTextBox1.CanUndo Then EditUndo.Enabled = True Else EditUndo.Enabled = False End If
These statements should appear in the menu item's Select
event handler (not in the Click
event handler) because they must be executed before the menu is displayed. The Select
event is triggered when a menu is opened. As a reminder, the Click
event is fired when you click an item and not when you open a menu. For more information on programming the events of a menu, see Chapter 6, "Working with Forms."
These two properties return the name of the action that can be undone or redone. The most common value of both properties is Typing
, which indicates that the Undo command will delete a number of characters. Another common value is Delete
, and some operations are named Unknown
. If you change the indentation of a paragraph on the control, this action's name is Unknown
. Even when an action's name is Unknown
the action can be undone with the Undo
method.
The following statement sets the caption of the Undo command to a string that indicates the action to be undone (Editor
is the name of a RichTextBox control):
If Editor.CanUndo Then EditUndo.Text = "Undo " & Editor.UndoActionName End If
To cut, copy, and paste text in the RichTextBox control, you can use the same techniques you use with the regular TextBox control. For example, you can replace the current selection by assigning a string to the SelectedText
property. The RichTextBox, however, provides a few useful methods for performing these operations. The Copy, Cut
, and Paste
methods perform the corresponding operations. The Cut
and Copy
methods are straightforward and require no arguments. The Paste
method accepts a single argument, which is the format of the data to be pasted. Because the data will come from the Clipboard, you can extract the format of the data in the Clipboard at the time and then call the CanPaste
method to find out whether the control can handle this type of data. If so, you can then paste them in the control by using the Paste
method.
This technique requires a bit of code because the Clipboard class doesn't return the format of the data in the Clipboard. You must call the following method of the Clipboard class to find out whether the data is of a specific type and then paste it on the control:
If Clipboard.GetDataObject.GetDataPresent(DataFormats.Text) Then RichTextBox1.Paste(DataFormats.GetFormat("Text") End If
This is a very simple case because we know that the RichTextBox control can accept text. For a robust application, you must call the GetDataPresent
method for each type of data your application should be able to handle. (You may not want to allow users to paste all types of data that the control can handle.) By the way, you can simplify the code with the help of the ContainsText/ContainsImage
and GetText/GetImage
methods of the My.Application.Clipboard object.
In the RTFPad project in this chapter, we'll use a structured exception handler to allow users to paste anything in the control. If the control can't handle it, the data won't be pasted in the control.
Creating a functional — even fancy — word processor based on the RichTextBox control is unexpectedly simple. The challenge is to provide a convenient interface that lets the user select text, apply attributes and styles to it, and then set the control's properties accordingly. The RTFPad sample application of this section does just that. You can download a copy from www.sybex.com/go/masteringvb2010
.
The RTFPad application (refer to Figure 7.7) is based on the TextPad application developed in Chapter 5, "Basic Windows Controls." It contains the same text-editing commands and some additional text-formatting commands that can be implemented only with the RichTextBox control; for example, it allows you to apply multiple fonts and styles to the text and, of course, multiple Undo/Redo operations.
The two TrackBar controls above the RichTextBox control manipulate the indentation of the text. We already explored this arrangement in the discussion of the TrackBar control in Chapter 5, but let's review the operation of the two controls again. Each TrackBar control has a width of 816 pixels, which is equivalent to 8.5 inches on a monitor that has a resolution of 96 dots per inch (dpi). The height of the TrackBar controls is 42 pixels, but unfortunately they can't be made smaller. The Minimum
property of both controls is 0, and the Maximum
property is 16. The TickFrequency
is 1. With these values, you can adjust the indentation in steps of ½ inch. Set the Maximum
property to 32 and you'll be able to adjust the indentation in steps of ¼ inch. It's not the perfect interface, as it's built for A4 pages in portrait orientation only. You can experiment with this interface to build an even more functional word processor.
Each time the user slides the top TrackBar control, the code sets the SelectionIndent
property to the proper percentage of the control's width. Because the SelectionHangingIndent
includes the value of the SelectionIndent
property, it also adjusts the setting of the SelectionHangingIndent
property. Listing 7.4 is the code that's executed when the upper TrackBar control is scrolled.
Example 7.4. Setting the SelectionIndent
property
Private Sub TrackBar1_Scroll(...) Handles TrackBar1.Scroll Editor.SelectionIndent = Convert.ToInt32(Editor.Width * (TrackBar1.Value / TrackBar1.Maximum)) Editor.SelectionHangingIndent = Convert.ToInt32(Editor.Width * (TrackBar2.Value / TrackBar2.Maximum) – Editor.SelectionIndent) End Sub
Editor
is the name of the RichTextBox control on the form. The code sets the control's indentation to the same percentage of the control's width, as indicated by the value of the top TrackBar control. It also does the same for the SelectionHangingIndent
property, which is controlled by the lower TrackBar control. If the user has scrolled the lower TrackBar control, the code sets the RichTextBox control's SelectionHangingIndent
property in the event handler, as presented in Listing 7.5.
Example 7.5. Setting the SelectionHangingIndent
property
Private Sub TrackBar2_Scroll(...) Handles TrackBar2.Scroll Editor.SelectionHangingIndent = Convert.ToInt32(Editor.Width * (TrackBar2.Value / TrackBar2.Maximum) - Editor.SelectionIndent) End Sub
Enter a few lines of text in the control, select one or more paragraphs, and check out the operation of the two sliders.
The Scroll
events of the two TrackBar controls adjust the text's indentation. The opposite action must take place when the user rests the pointer on another paragraph: The sliders' positions must be adjusted to reflect the indentation of the selected paragraph. The selection of a new paragraph is signaled to the application by the SelectionChanged
event. The statements of Listing 7.6, which are executed from within the SelectionChanged
event, adjust the two slider controls to reflect the indentation of the text.
Example 7.6. Setting the slider controls
Private Sub Editor_SelectionChanged(...) Handles Editor.SelectionChanged If Editor.SelectionIndent = Nothing Then TrackBar1.Value = TrackBar1.Minimum TrackBar2.Value = TrackBar2.Minimum Else
TrackBar1.Value = Convert.ToInt32( Editor.SelectionIndent * TrackBar1.Maximum / Editor.Width) TrackBar2.Value = Convert.ToInt32( _ (Editor.SelectionHangingIndent / Editor.Width) * TrackBar2.Maximum + TrackBar1.Value) End If End Sub
If the user selects multiple paragraphs with different indentations, the SelectionIndent
property returns Nothing. The code examines the value of this property and, if it's Nothing, moves both controls to the left edge. This way, the user can slide the controls and set the indentations for multiple paragraphs. Some applications make the handles gray to indicate that the selected text doesn't have uniform indentation, but unfortunately you can't gray the sliders and keep them enabled. Of course, you can always design a custom control. This wouldn't be a bad idea, especially if you consider that the TrackBar controls are too tall for this type of interface and can't be made very narrow (as a result, the interface of the RTFPad application isn't very elegant).
The RTFPad application's File menu contains the usual Open, Save, and Save As commands, which are implemented with the control's LoadFile
and SaveFile
methods. Listing 7.7 shows the implementation of the Open command in the File menu.
Example 7.7. The Open command
Private Sub OpenToolStripMenuItem_Click(...) Handles OpenToolStripMenuItem.Click If DiscardChanges() Then OpenFileDialog1.Filter = "RTF Files|*.RTF|DOC Files|*.DOC|" & "Text Files|*.TXT|All Files|*.*" If OpenFileDialog1.ShowDialog() = DialogResult.OK Then fName = OpenFileDialog1.FileName Editor.LoadFile(fName) Editor.Modified = False End If End If End Sub
The fName
variable is declared on the form's level and holds the name of the currently open file. This variable is set every time a new file is successfully opened, and it's used by the Save command to automatically save the open file without prompting the user for a filename.
DiscardChanges()
is a function that returns a Boolean value, depending on whether the control's contents can be discarded. The function examines the Editor
control's Modified
property. If True, it prompts users as to whether they want to discard the edits. Depending on the value of the Modified
property and the user response, the function returns a Boolean value. If the DiscardChanges()
function returns True, the program goes on and opens a new document. If the function returns False, the program aborts the operation to give the user a chance to save the document. Listing 7.8 shows the DiscardChanges()
function.
Example 7.8. The DiscardChanges()
function
Function DiscardChanges() As Boolean If Editor.Modified Then Dim reply As MsgBoxResult reply = MsgBox( "Text hasn't been saved. Discard changes?", MsgBoxStyle.YesNo) If reply = MsgBoxResult.No Then Return False Else Return True End If Else Return True End If End Function
The Modified
property becomes True after the first character is typed and isn't reset back to False. The RichTextBox control doesn't handle this property very intelligently and doesn't reset it to False even after saving the control's contents to a file. The application's code sets the Editor.Modified
property to False after creating a new document as well as after saving the current document.
The Save As command (see Listing 7.9) prompts the user for a filename and then stores the Editor
control's contents to the specified file. It also sets the fName
variable to the file's path so that the Save command can use it.
Example 7.9. The Save As command
Private Sub SaveAsToolStripMenuItem_Click(...) Handles SaveAsToolStripMenuItem.Click SaveFileDialog1.Filter = "RTF Files|*.RTF|DOC Files" & "|*.DOC|Text Files|*.TXT|All Files|*.*" SaveFileDialog1.DefaultExt = "RTF" If SaveFileDialog1.ShowDialog() = DialogResult.OK Then fName = SaveFileDialog1.FileName
Editor.SaveFile(fName) Editor.Modified = False End If End Sub
The Save command's code is similar, only it doesn't prompt the user for a filename. It calls the SaveFile
method, passing the fName
variable as an argument. If the fName
variable has no value (in other words, if a user attempts to save a new document by using the Save command), the code activates the event handler of the Save As command automatically and resets the control's Modified
property to False. Listing 7.10 shows the code behind the Save command.
The Edit menu contains the usual commands for exchanging data through the Clipboard (Copy, Cut, Paste), Undo/Redo commands, and a Find command to invoke the Search & Replace dialog box. All the commands are almost trivial, thanks to the functionality built into the control. The basic Cut, Copy, and Paste commands call the RichTextBox control's Copy, Cut
, and Paste
methods to exchange data through the Clipboard. Listing 7.11 shows the implementation of the Paste command.
Example 7.11. The Paste command
Private Sub PasteToolStripMenuItem_Click(...) Handles PasteToolStripMenuItem.Click Try Editor.Paste() Catch exc As Exception MsgBox( "Can't paste current clipboard's contents. " & "Try pasting the data in some other format.") End Try End Sub
As you may recall from the discussion of the Paste command, using the CanPaste
method isn't trivial, you have to handle each data type differently. By using an exception handler, you allow the user to paste all types of data that the RichTextBox control can accept and display a message when an error occurs. Using exceptions for programming application logic can be quite costly, but in this case it's acceptable because the RTFPad editor is a desktop application serving a single user. A delay of a few milliseconds in this case should not make a huge difference.
For a more robust solution though, you might wish to handle each data type separately using the CanPaste
method. That way, you can provide the user with much more precise feedback over the problem that caused the error; that is, the exact format of the data in the Clipboard they are trying to paste but the RichTextBox is not able to handle. That way you can save the user from having to guess the format your application can handle.
The Undo and Redo commands of the Edit menu are coded as follows. First, the name of the action to be undone or redone is displayed in the Edit menu. When the Edit menu is selected, the DropDownOpened
event is fired. This event takes place before the Click
event, so I inserted a few lines of code that read the name of the most recent action that can be undone or redone and print it next to the Undo or Redo command's caption. If there's no such action, the program will disable the corresponding command. Listing 7.12 is the code that's executed when the Edit menu is dropped.
Example 7.12. Setting the captions of the Undo and Redo commands
Private Sub EditToolStripMenuItem_DropDownOpened(...) Handles EditToolStripMenuItem.DropDownOpened If Editor.UndoActionName <> "" Then UndoToolStripMenuItem.Text = "Undo " & Editor.UndoActionName UndoToolStripMenuItem.Enabled = True Else UndoToolStripMenuItem.Text = "Undo" UndoToolStripMenuItem.Enabled = False End If If Editor.RedoActionName <> "" Then RedoToolStripMenuItem.Text = "Redo" & Editor.RedoActionName RedoToolStripMenuItem.Enabled = True Else RedoToolStripMenuItem.Text = "Redo" RedoToolStripMenuItem.Enabled = False End If End Sub
When the user selects one of the Undo or Redo commands, the code simply calls the appropriate method from within the menu item's Click
event handler, as shown in Listing 7.13.
Example 7.13. Undoing and redoing actions
Private Sub RedoToolStripMenuItem_Click(...) Handles RedoToolStripMenuItem.Click If Editor.CanRedo Then Editor().Redo() End Sub Private Sub UndoToolStripMenuItem_Click(...) Handles UndoToolStripMenuItem.Click If Editor.CanUndo Then Editor.Undo() End Sub
Calling the CanUndo
and CanRedo
method is unnecessary; if the corresponding action can't be performed, the two menu items will be disabled, but an additional check does no harm.
The commands of the Format menu control the alignment and the font attributes of the current selection. The Font command displays the Font dialog box and then assigns the font selected by the user to the current selection. Listing 7.14 shows the code behind the Font command.
Example 7.14. The Font command
Private Sub FontToolStripMenuItem_Click(...) Handles FontToolStripMenuItem.Click If Not Editor.SelectionFont Is Nothing Then FontDialog1.Font = Editor.SelectionFont Else FontDialog1.Font = Nothing End If FontDialog1.ShowApply = True If FontDialog1.ShowDialog() = DialogResult.OK Then Editor.SelectionFont = FontDialog1.Font End If End Sub
Notice that the code preselects a font in the dialog box, the font of the current selection. If the current selection isn't formatted with a single font, no font is preselected.
To enable the Apply button of the Font dialog box, set the control's ShowApply
property to True and insert the following statement in its Apply
event handler:
Private Sub FontDialog1_Apply(...) Handles FontDialog1.Apply Editor.SelectionFont = FontDialog1.Font Editor.SelectionColor = FontDialog1.Color End Sub
The options of the Align menu set the RichTextBox control's SelectionAlignment
property to different members of the HorizontalAlignment
enumeration. The Align
Editor.SelectionAlignment = HorizontalAlignment.Left
The Find command in the Edit menu opens the dialog box shown in Figure 7.9, which performs search-and-replace operations (whole-word or case-sensitive match or both). The Search & Replace form (it's the frmFind
form in the project) has its TopMost
property set to True so that it remains visible while it's open, even if it doesn't have the focus. The code behind the buttons on this form is quite similar to the code for the Find & Replace dialog box of the TextPad application, with one basic difference: the RTFPad project's code uses the RichTextBox control's Find
method; the simple TextBox control doesn't provide an equivalent method and we had to use the methods of the String class to perform the same operations. The Find
method of the RichTextBox control performs all types of searches, and some of its options are not available with the IndexOf
method of the String class.
To invoke the Search & Replace dialog box, the code calls the Show
method of the frmFind
form, as discussed in Chapter 5, via the following statement:
frmFind.Show()
The Find
method of the RichTextBox control allows you to perform case-sensitive or -insensitive searches as well as search for whole words only. These options are specified through an argument of the RichTextBoxFinds
type. The SetSearchMode()
function (see Listing 7.15) examines the settings of the two check boxes at the bottom of the form and sets the mode
variable, which represents the Find
method's search mode.
Example 7.15. Setting the search options
Function SetSearchMode() As RichTextBoxFinds Dim mode As RichTextBoxFinds = RichTextBoxFinds.None If chkCase.Checked = True Then mode = mode Or RichTextBoxFinds.MatchCase End If If chkWord.Checked = True Then mode = mode Or RichTextBoxFinds.WholeWord End If Return mode End Function
The Click
event handlers of the Find and Find Next buttons call this function to retrieve the constant that determines the type of search specified by the user on the form. This value is then passed to the Find
method. Listing 7.16 shows the code behind the Find and Find Next buttons.
Example 7.16. The Find and Find Next commands
Private Sub bttnFind_Click(...) Handles bttnFind.Click Dim wordAt As Integer Dim srchMode As RichTextBoxFinds srchMode = SetSearchMode() wordAt = frmEditor.Editor.Find( txtSearchWord.Text, 0, srchMode) If wordAt = −1 Then MsgBox("Can't find word") Exit Sub End If frmEditor.Editor.Select(wordAt, txtSearchWord.Text.Length) bttnFindNext.Enabled = True bttnReplace.Enabled = True bttnReplaceAll.Enabled = True frmEditor.Editor.ScrollToCaret() End Sub Private Sub bttnFindNext_Click(...) Handles bttnFindNext.Click Dim selStart As Integer Dim srchMode As RichTextBoxFinds srchMode = SetSearchMode() selStart = frmEditor.Editor.Find( txtSearchWord.Text, frmEditor.Editor.SelectionStart + 2, srchMode)
If selStart = −1 Then MsgBox("No more matches") Exit Sub End If frmEditor.Editor.Select( selStart, txtSearchWord.Text.Length) frmEditor.Editor.ScrollToCaret() End Sub
Notice that both event handlers call the ScrollToCaret
method to force the selected text to become visible — should the Find
method locate the desired string outside the visible segment of the text.
The TreeView and ListView controls are among the more advanced Windows controls and they certainly are more difficult to program than the others discussed. However, these two controls are the basic makings of unique user interfaces, as you will see in the examples in the following sections. The ListView and TreeView controls are discussed in detail in the tutorial "The ListView and TreeView controls," which is available for download from www.sybex.com/go/masteringvb2010
. In this chapter, you will find an introduction to these two controls and their basic properties and methods. For more information on using these controls in your interface and interesting examples, please read the tutorial.
Figure 7.10 shows the TreeView and ListView controls used in tandem. What you see in Figure 7.10 is Windows Explorer, a utility for examining and navigating your hard disk's structure. The left pane, where the folders are displayed, is a TreeView control. The folder names are displayed in a manner that reflects their structure on the hard disk. You can expand and contract certain branches and view only the segment(s) of the tree structure you're interested in.
Figure 7.10. Windows Explorer is made up of a TreeView (left pane) and a ListView (right pane) control.
The right pane is a ListView control. The items on the ListView control can be displayed in five ways (as large or small icons, as a list, on a grid, or tiled). They are the various views you can set through the View menu of Windows Explorer. Although most people prefer to look at the contents of the folders as icons, the most common view is the Details view, which displays not only filenames, but also their attributes. In the Details view, the list can be sorted according to any of its columns, making it easy for the user to locate any item based on various criteria (file type, size, creation date, and so on).
The TreeView control implements a data structure known as a tree. A tree is the most appropriate structure for storing hierarchical information. The organizational chart of a company, for example, is a tree structure. Every person reports to another person above him or her, all the way to the president or CEO. Figure 7.11 depicts a possible organization of continents, countries, and cities as a tree. Every city belongs to a country, and every country to a continent. In the same way, every computer file belongs to a folder that may belong to an even bigger folder, and so on up to the drive level. You can't draw large tree structures on paper, but it's possible to create a similar structure in the computer's memory without size limitations.
Each item in the tree of Figure 7.11 is called a node, and nodes can be nested to any level. Oddly, the top node is the root of the tree, and the subordinate nodes are called child nodes. If you try to visualize this structure as a real tree, think of it as an upside-down tree with the branches emerging from the root. The end nodes, which don't lead to any other nodes, are called leaf nodes or end nodes.
To locate a city, you must start at the root node and select the continent to which the city belongs. Then you must find the country (in the selected continent) to which the city belongs. Finally, you can find the city you're looking for. If it's not under the appropriate country node, it doesn't exist.
You can also start with a city and find its country. The country node is the city node's parent node. Notice that there is only one route from child nodes to their parent nodes, which means that you can instantly locate the country or continent of a city. The data shown in Figure 7.11 is shown in Figure 7.12 in a TreeView control. Only the nodes we're interested in are expanded. The plus sign indicates that the corresponding node contains child nodes. To view them, end users click the button with the plus sign and expand the node.
The tree structure is ideal for data with parent-child relations (relations that can be described as belongs to or owns). The continents-countries-cities data is a typical example. The folder structure on a hard disk is another typical example. Any given folder is the child of another folder or the root folder.
Maintaining a tree structure is a fundamental operation in software design; computer science students spend a good deal of their time implementing tree structures. Fortunately, with Visual Basic you don't have to implement tree structures on your own. The TreeView control is a mechanism for storing hierarchically structured data in a control with a visible interface. The TreeView control hides (or encapsulates, in object-oriented terminology) the details of the implementation and allows you to set up tree structures with a few lines of code — in short, all the gain without the pain (almost).
The ListView control implements a simpler structure, known as a list. A list's items aren't structured in a hierarchy; they are all on the same level and can be traversed serially, one after the other. You can also think of the list as a multidimensional array, but the list offers more features. A list item can have subitems and can be sorted according to any column. For example, you can set up a list of customer names (the list's items) and assign a number of subitems to each customer: a contact, an address, a phone number, and so on. Or you can set up a list of files with their attributes as subitems. Figure 7.13 shows a Windows folder mapped on a ListView control. Each file is an item, and its attributes are the subitems. As you already know, you can sort this list by filename, size, file type, and so on. All you have to do is click the header of the corresponding column.
The ListView control is a glorified ListBox control. If all you need is a control to store sorted objects, use a ListBox control. If you want more features, such as storing multiple items per row, sorting them in different ways, or locating them based on any subitem's value, you must consider the ListView control. You can also look at the ListView control as a view-only grid.
The TreeView and ListView controls are commonly used along with the ImageList control. The ImageList control is a simple control for storing images so they can be retrieved quickly and used at runtime. You populate the ImageList control with the images you want to use on your interface, usually at design time, and then you recall them by an index value at runtime.
Let's start our discussion of the TreeView control with a few simple properties that you can set at design time. To experiment with the properties discussed in this section, open the TreeViewDemo project, available for download from www.sybex.com/go/masteringvb2010
. The project's main form is shown in Figure 7.14. After setting some properties (they are discussed next), run the project and click the Populate button to populate the control. After that, you can click the other buttons to see the effect of the various property settings on the control.
Figure 7.14. The TreeViewDemo project demonstrates the basic properties and methods of the TreeView control.
Here are the basic properties that determine the appearance of the control:
CheckBoxes
If this property is True, a check box appears in front of each node. If the control displays check boxes, you can select multiple nodes; otherwise, you're limited to a single selection.
FullRowSelect
This True/False value determines whether a node will be selected even if the user clicks outside the node's caption.
HideSelection
This property determines whether the selected node will remain highlighted when the focus is moved to another control. By default, the selected node doesn't remain highlighted when the control loses the focus.
HotTracking
This property is another True/False value that determines whether nodes are highlighted as the pointer hovers over them. When it's True, the TreeView control behaves like a web document with the nodes acting as hyperlinks — they turn blue while the pointer hovers over them. Use the NodeMouseHover
event to detect when the pointer hovers over a node.
Indent
This property specifies the indentation level in pixels. The same indentation applies to all levels of the tree — each level is indented by the same number of pixels with respect to its parent level.
PathSeparator
A node's full name is made up of the names of its parent nodes separated by a backslash. To use a different separator, set this property to the desired symbol.
ShowLines
The ShowLines
property is a True/False value that determines whether the control's nodes will be connected to its parent items with lines. These lines help users visualize the hierarchy of nodes, and it's customary to display them.
ShowPlusMinus
The ShowPlusMinus
property is a True/False value that determines whether the plus/minus button is shown next to the nodes that have children. The plus button is displayed when the node is collapsed, and it causes the node to expand when clicked. Likewise, the minus sign is displayed when the node is expanded, and it causes the node to collapse when clicked. Users can also expand the current node by pressing the left-arrow button and collapse it with the right-arrow button.
ShowRootLines
This is another True/False property that determines whether there will be lines between each node and root of the tree view. Experiment with the ShowLines
and ShowRootLines
properties to find out how they affect the appearance of the control.
Sorted
This property determines whether the items in the control will be automatically sorted. The control sorts each level of nodes separately. In our globe example, it will sort the continents, then the countries within each continent, and then the cities within each country.
Let's look now at the process of populating the TreeView control. Adding an initial collection of nodes to a TreeView control at design time is trivial. Locate the Nodes
property in the Properties window, and you'll see that its value is Collection. To add items, click the ellipsis button, and the TreeNode Editor dialog box will appear, as shown in Figure 7.15. To add a root item, just click the Add Root button. The new item will be named Node0
by default. You can change its caption by selecting the item in the list and setting its Text
property accordingly. You can also change the node's Name
property, and you can change the node's appearance by using the NodeFont, FontColor
, and ForeColor
properties.
Follow these steps to enter the root node with the string Globe
, a child node for Europe, and two more nodes under Europe: Germany and Italy. I'm assuming that you're starting with a clean control. If your TreeView control contains any items, clear them all by selecting one item at a time in the list and pressing the Delete key, or click the delete button (the one with the X icon) on the dialog box.
Click the Add Root button first to add the node Node0
. Select it with the mouse, and its properties appear in the right pane of the TreeNode Editor window. Here you can change the node's Text
property to Globe
. You can specify the appearance of each node by setting its font and fore/background colors.
Then click the Add Child button, which adds a new node under the Globe
root node. Select it with the mouse as before, and change its Text
property to Europe
. Then select the newly added node in the list and click the Add Child button again. Name the new node Germany
. You've successfully added a small hierarchy of nodes. To add another node under Europe, select the Europe node in the list and click the Add Child button again. Name the new item Italy
. Continue adding a few cities under each country to complete the tree.
Click the OK button to close the TreeNode Editor's window and return to your form. The nodes you added to the TreeView control are there, but they're collapsed. Only the root nodes are displayed with the plus sign in front of their names. Click the plus sign to expand the tree and see its child nodes. The TreeView control behaves the same at design time as it does at runtime — as far as navigating the tree goes, at least.
Adding items to the control at runtime is a bit more involved. All the nodes belong to the control's Nodes
collection, which is made up of TreeNode objects. To access the Nodes
collection, use the following expression, where TreeView1
is the control's name and Nodes
is a collection of TreeNode objects:
TreeView1.Nodes
This expression returns a collection of TreeNode objects and exposes the proper members for accessing and manipulating the individual nodes. The control's Nodes
property is the collection of all root nodes.
The following statements print the strings shown highlighted below them (these strings are not part of the statements; they're the output that the statements produce):
Debug.WriteLine(TreeView1.Nodes(0).Text) Globe Debug.WriteLine(TreeView1.Nodes(0).Nodes(0).Text) Europe Debug.WriteLine(TreeView1.Nodes(0).Nodes(0).Nodes(1).Text) Italy
To add a new node to the Nodes
collection use the Add
method, which accepts as an argument a string or a TreeNode object, and returns a TreeNode object that represents the newly added node. The simplest form of the Add
method is
newNode = Nodes.Add(nodeCaption)
where nodeCaption
is a string that will be displayed on the control. Another form of the Add
method allows you to add a TreeNode object directly (nodeObj
is a properly initialized TreeNode variable):
newNode = Nodes.Add(nodeObj)
To use this form of the method, you must first declare and initialize a TreeNode object:
Dim nodeObj As New TreeNode nodeObj.Text = "Tree Node" nodeObj.ForeColor = Color.BlueViolet TreeView1.Nodes.Add(nodeObj)
The last overloaded form of the Add
method allows you to specify the index in the current Nodes
collection, where the node will be added:
newNode = Nodes.Add(index, nodeObj)
The nodeObj
TreeNode object must be initialized as usual.
To add a child node to the root node, use a statement such as the following:
TreeView1.Nodes(0).Nodes.Add("Asia")
To add a country under Asia, use a statement such as the following:
TreeView1.Nodes(0).Nodes(1).Nodes.Add("Japan")
The expressions can get quite lengthy. The proper way to add child items to a node is to create a TreeNode variable that represents the parent node, under which the child nodes will be added. Let's say that the ContinentNode
variable in the following example represents the node Europe:
Dim ContinentNode As TreeNode ContinentNode = TreeView1.Nodes(0).Nodes(2)
Then you can add child nodes to the ContinentNode
node:
ContinentNode.Nodes.Add("France") ContinentNode.Nodes.Add("Germany")
To add yet another level of nodes, the city nodes, create a new variable that represents a specific country. The Add
method actually returns a TreeNode object that represents the newly added node, so you can add a country and a few cities by using statements such as the following:
Dim CountryNode As TreeNode CountryNode = ContinentNode.Nodes.Add("Germany") CountryNode.Nodes.Add("Berlin") CountryNode.Nodes.Add("Frankfurt")
The ListView control is similar to the ListBox control except that it can display its items in many forms, along with any number of subitems for each item. To use the ListView control in your project, place an instance of the control on a form and then set its basic properties, which are described in the following list:
View
and Alignment
Two properties determine how the various items will be displayed on the control: the View
property, which determines the general appearance of the items, and the Alignment
property, which determines the alignment of the items on the control's surface. The View
property can have one of the values shown in Table 7.3.
Table 7.3. View
property settings
Setting | Description |
---|---|
| (Default) Each item is represented by an icon and a caption below the icon. |
| Each item is represented by a small icon and a caption that appears to the right of the icon. |
| Each item is represented by a caption. |
| Each item is displayed in a column with its subitems in adjacent columns. |
| Each item is displayed with an icon and its subitems to the right of the icon. This view is available only on Windows XP and Windows Server 2003. |
The Alignment
property can have one of the settings shown in Table 7.4.
Table 7.4. Alignment
property settings
Setting | Description |
---|---|
| When an item is moved on the control, the item remains where it is dropped. |
| Items are aligned to the left side of the control. |
| Items are aligned to an invisible grid on the control. When the user moves an item, the item moves to the closest grid point on the control. |
| Items are aligned to the top of the control. |
HeaderStyle
This property determines the style of the headers in Details view. It has no meaning when the View
property is set to anything else because only the Details view has columns. The possible settings of the HeaderStyle
property are shown in Table 7.5.
AllowColumnReorder
This property is a True/False value that determines whether the user can reorder the columns at runtime, and it's meaningful only in Details view. If this property is set to True, the user can move a column to a new location by dragging its header with the mouse and dropping it in the place of another column.
Activation
This property, which specifies how items are activated with the mouse, can have one of the values shown in Table 7.6.
Table 7.6. Activation
property settings
Setting | Description |
---|---|
| Items are activated with a single click. When the cursor is over an item, it changes shape and the color of the item's text changes. |
| (Default) Items are activated with a double-click. No change in the selected item's text color takes place. |
| Items are activated with a double-click and their text changes color as well. |
FullRowSelect
This property is a True/False value, indicating whether the user can select an entire row or just the item's text, and it's meaningful only in Details view. When this property is False, only the first item in the selected row is highlighted.
GridLines
Another True/False property. If it's True, grid lines between items and subitems are drawn. This property is meaningful only in Details view.
Groups
The items of the ListView control can be grouped into categories. To use this feature, you must first define the groups by using the control's Groups
property, which is a collection of strings. You can add as many members to this collection as you want. After that, as you add items to the ListView control, you can specify the group to which they belong. The control will group the items of the same category together and display the group title above each group. You can easily move items between groups at runtime by setting the Groups
property for the corresponding item to the name of the desired group.
LabelEdit
The LabelEdit
property lets you specify whether the user will be allowed to edit the text of the items. The default value of this property is False. Notice that the LabelEdit
property applies to the item's Text
property only; you can't edit the subitems (unfortunately, you can't use the ListView control as an editable grid).
MultiSelect
A True/False value, indicating whether the user can select multiple items from the control. To select multiple items, click them with the mouse while holding down the Shift or Ctrl key. If the control's ShowCheckboxes
property is set to True, users can select multiple items by marking the check box in front of the corresponding item(s).
Scrollable
A True/False value that determines whether the scroll bars are visible. Even if the scroll bars are invisible, users can still bring any item into view. All they have to do is select an item and then press the arrow keys as many times as needed to scroll the desired item into view.
Sorting
This property determines how the items will be sorted, and its setting can be None, Ascending, or Descending. To sort the items of the control, call the Sort
method, which sorts the items according to their caption. It's also possible to sort the items according to any of their subitems, as explained later in this chapter.
To display items in Details view, you must first set up the appropriate columns. The first column corresponds to the item's caption, and the following columns correspond to its subitems. If you don't set up at least one column, no items will be displayed in Details view. Conversely, the Columns
collection is meaningful only when the ListView control is used in Details view.
The items of the Columns
collection are of the ColumnHeader type. The simplest way to set up the appropriate columns is to do so at design time by using a visual tool. Locate and select the Columns
property in the Properties window, and click the ellipsis button next to the property. The ColumnHeader Collection Editor dialog box, shown in Figure 7.16, will appear, and you can use it to add and edit the appropriate columns.
Adding columns to a ListView control and setting their properties through the dialog box shown in Figure 7.16 is quite simple. Don't forget to size the columns according to the data you anticipate storing in them and to set their headers. You can also add columns from within your code at runtime, a topic that's discussed in the tutorial "The ListView and TreeView Controls," available for download from www.sybex.com/go/masteringvb2010
.
As with the TreeView control, the ListView control can be populated either at design time or at runtime. To add items at design time, click the ellipsis button next to the ListItems
property in the Properties window. When the ListViewItem Collection Editor dialog box pops up, you can enter the items, including their subitems, as shown in Figure 7.17.
Click the Add button to add a new item. Each item has subitems, which you can specify as members of the SubItems
collection. To add an item with three subitems, you must populate the item's SubItems
collection with the appropriate elements. Click the ellipsis button next to the SubItems
property in the ListViewItem Collection Editor; the ListViewSubItem Collection Editor will appear. This dialog box is similar to the ListViewItem Collection Editor dialog box, and you can add each item's subitems. Assuming that you have added the item called Item 1
in the ListViewItem Collection Editor, you can add these subitems: Item 1-a
, Item 1-b
, and Item 1-c
. The first subitem (the one with zero index) is actually the main item of the control.
Notice that you can set other properties such as the color and font for each item, the check box in front of the item that indicates whether the item is selected, and the image of the item. Use this window to experiment with the appearance of the control and the placement of the items, especially in Details view because subitems are visible only in this view. Even then, you won't see anything unless you specify headers for the columns. Note that you can add more subitems than there are columns in the control. Some of the subitems will remain invisible.
Unlike the TreeView control, the ListView control allows you to specify a different appearance for each item and each subitem. To set the appearance of the items, use the Font, BackColor
, and ForeColor
properties of the ListViewItem object.
All the items on the ListView control form a collection: the Items
collection. This collection exposes the typical members of a collection that let you manipulate the control's items. These members are discussed next.
Add method
This method adds a new item to the Items
collection. The syntax of the Add
method is as follows:
ListView1.Items.Add(caption)
You can also specify the index of the image to be used, along with the item and a collection of subitems to be appended to the new item, by using the following form of the Add
method, where imageIndex
is the index of the desired image on the associated ImageList control:
ListView1.Items.Add(caption, imageIndex)
Finally, you can create a ListViewItem object in your code and then add it to the ListView control by using the following form of the Add
method:
ListView1.Items.Add(listItemObj)
The following statements create a new item, set its individual subitems, and then add the newly created ListViewItem object to the control:
Dim LItem As New ListViewItem LItem.Text = "new item" LItem.SubItems.Add("sub item 1a") LItem.SubItems.Add("sub item 1b") LItem.SubItems.Add("sub item 1c") ListView1.Items.Add(LItem)
Count property
Returns the number of items in the collection.
Item property
Retrieves an item specified by an index value.
Clear method
Removes all the items from the collection.
Remove method
Removes an item from the collection.
Each item in the ListView control may have one or more subitems. You can think of the item as the key of a record and the subitems as the other fields of the record. The subitems are displayed only in Details mode, but they are available to your code in any view. For example, you can display all items as icons and, when the user clicks an icon, show the values of the selected item's subitems on other controls.
To access the subitems of a given item, use its SubItems
collection. The following statements add an item and three subitems to the ListView1
control:
Dim LItem As ListViewItem LItem = ListView1.Items.Add("Alfred's Futterkiste") LItem.SubItems.Add("Maria Anders") LItem.SubItems.Add("030-0074321") LItem.SubItems.Add("030-0076545")
To access the SubItems
collection, you need a reference to the item to which the subitems belong. The Add
method returns a reference to the newly added item, the LItem
variable, which is then used to access the item's subitems, as shown in the preceding code segment.
Displaying the subitems on the control requires some overhead. Subitems are displayed only in Details view mode. However, setting the View
property to Details is not enough. You must first create the columns of the Details view, as explained earlier. The ListView control displays only as many subitems as there are columns in the control. The first column, with the header Company, displays the items of the list. The following columns display the subitems. Moreover, you can't specify which subitem will be displayed under each header. The first subitem (Maria Anders in the preceding example) will be displayed under the second header, the second subitem (030-0074321 in the same example) will be displayed under the third header, and so on. At runtime, the user can rearrange the columns by dragging them with the mouse. To disable the rearrangement of the columns at runtime, set the control's AllowColumnReorder
property to False (its default value is True).
Unless you set up each column's width, they will all have the same width. The width of individual columns is specified in pixels, and you can set it to a percentage of the total width of the control, especially if the control is docked to the form. The following code sets up a ListView control with four headers, all having the same width:
Dim LWidth = ListView1.Width - 5 Dim headers = { New ColumnHeader() With {.Text = "Company", .Width = LWidth / 4}, New ColumnHeader() With {.Text = "Contact", .Width = LWidth / 4}, New ColumnHeader() With {.Text = "Phone", .Width = LWidth / 4}, New ColumnHeader() With {.Text = "Fax", .Width = LWidth / 4} } ListView1.Columns.AddRange(headers) ListView1.View = View.Details
The first header corresponds to the item (not a subitem). The number of headers you set up must be equal to the number of subitems you want to display on the control, plus one. The constant 5 is subtracted to compensate for the width of the column separators. If the control is anchored to the vertical edges of the form, you must execute these statements from within the form's Resize
event handler so that the columns are resized automatically as the control is resized.
You can also sort a ListView control with the Sort method, which sorts the list's items, and the Sorting property, which determines how the items will be sorted. For more information on sorting the control's items, see the tutorial "The ListView and TreeView Controls," available for download from www.sybex.com/go/masteringvb2010
.
The user can select multiple items from a ListView control by default. Even though you can display a check mark in front of each item, it's not customary. Multiple items in a ListView control are selected with the mouse while holding down the Ctrl or Shift key.
The selected items form the SelectedListItemCollection
collection, which is a property of the control. You can iterate through this collection with a For ... Next
loop or through the enumerator object exposed by the collection. Listing 7.17 is the code behind the Selected Items button of the ListViewDemo project. It goes through the selected items with a For Each ... Next
loop and displays each one of them, along with its subitems, in the Output window. Notice that you can select multiple items in any view, even when the subitems are not visible. They're still there, however, and they can be retrieved through the SubItems
collection.
Example 7.17. Iterating the selected items on a ListView control
Private Sub bttnIterate_Click(...) Handles bttnIterate.Click Dim LItem As ListViewItem Dim LItems As ListView.SelectedListViewItemCollection LItems = ListView1.SelectedItems For Each LItem In LItems Debug.Write(LItem.Text & vbTab) Debug.Write(LItem.SubItems(0).ToString & vbTab) Debug.Write(LItem.SubItems(1).ToString & vbTab) Debug.WriteLine(LItem.SubItems(2).ToString & vbTab) Next End Sub
To demonstrate how to use the ListView and TreeView controls in tandem, which is how they commonly used, see the discussion of the CustomExplorer sample application, which is discussed in the tutorial "The ListView and TreeView controls." It's a fairly advanced example, but I included it for the most ambitious readers. It can also be used as the starting point for many custom applications, so give it a try.
The CustomExplorer project, shown in Figure 7.18, displays a structured list of folders in the left pane and the files in the selected folder in the right pane. The left pane is populated when the application starts. You can expand any folder in this pane and view its subfolders. To view the files in a folder, click the folder name and the right pane will be populated with the names of the selected folder's files along with other data, such as the file size, date of creation, and date of last modification.
Figure 7.18. The CustomExplorer project demonstrates how to combine a TreeView and a ListView control on the same form.
The CustomExplorer project is not limited to displaying folders and files; you can populate the two controls with data from several sources. For example, you can display customers in the left pane (and organize them by city or state) and display their related data, such as invoices and payments, in the right pane. Or you can populate the left pane with product names and the right pane with the respective sales. In general, you can use the project as an interface for many types of applications. You can even use it as a custom Explorer to add features that are specific to your applications.
Windows applications use certain controls to prompt users for common information, such as filenames, colors, and fonts. Visual Studio provides a set of controls that are grouped in the Dialogs section of the Toolbox. All common dialog controls provide a ShowDialog
method, which displays the corresponding dialog box in a modal way. The ShowDialog
method returns a value of the DialogResult
type, which indicates how the dialog box was closed, and you should examine this value before processing the data.
Your application needs to open an existing file. How will you prompt users for the file's name?
You're developing an application that encrypts multiple files (or resizes many images) in batch mode. How will you prompt the user for the files to be processed?
The Color and Font dialog boxes allow you to prompt users for a color value and a font, respectively. Before showing the corresponding dialog box, set its Color
or Font
property according to the current selection, and then call the control's ShowDialog
method.
How will you display color attributes in the Color dialog box when you open it? How will you display the attributes of the selected text's font in the Font dialog box when you open it?
The RichTextBox control is an enhanced TextBox control that can display multiple fonts and styles, format paragraphs with different styles, and provide a few more-advanced text-editing features. Even if you don't need the formatting features of this control, you can use it as an alternative to the TextBox control. At the very least, the RichTextBox control provides more editing features, a more-useful undo function, and more-flexible search features.
You want to display a document with a title in large, bold type, followed by a couple of items in regular style. Which statements will you use to create a document like this on a RichTextBox control?
Document's Title
Description for item 1
Description for item 2
The TreeView control is used to display a list of hierarchically structured items. Each item in the TreeView control is represented by a TreeNode object. To access the nodes of the TreeView control, use the TreeView.Nodes
collection. The nodes under a specific node (in other words, the child nodes) form another collection of Node objects, which you can access by using the expression TreeView.Nodes(i).Nodes
. The basic property of the Node object is the Text
property, which stores the node's caption. The Node object exposes properties for manipulating its appearance (its foreground/background color, its font, and so on).
How will you set up a TreeView control with a book's contents at design time?
The ListView control stores a collection of ListViewItem objects, which form the Items collection, and can display them in several modes, as specified by the View
property. Each ListViewItem object has a Text
property and the SubItems collection. The subitems are not visible at runtime unless you set the control's View
property to Details and set up the control's Columns collection. There must be a column for each subitem you want to display on the control.
How will you set up a ListView control with three columns to display names, email addresses, and phone numbers at design time?
How would you populate the same control with the same data at runtime?
18.189.189.67