Advanced Visual Basic .NET - Project 13
- Adding Tooltips support
- Dynamically creating and destroying Controls
- Creating Delegate procedures for a collection of controls
- Using Context Menus
- Printing
Project 13 - Tying up some Loose Ends
Removing your old project and creating a new one
Run Visual Basis .NET and open your Solution (<your name>.sln).
Right-click on the MyWebBrowser project in the Solution Explorer window and select Remove from the context menu. Do not save this project if prompted to do so. Your solution should now be empty.
Drop down the File menu and select New Project under the Add Project menu item. When the Add New Project dialog appears, be sure that the Visual Basic Projects folder is open in the Project Types pane, and that the Windows Application template is selected in the Templates pane. Type Project13-Loose Ends in the Name textbox. Then click the OK button. This creates a new folder inside the \Visual Studio Projects\<Your Name> folder named Project13-Loose Ends:
...My Documents\Visual Studio Projects\<Your Name>\Project13-Loose Ends.
Note: When class is over, be sure to follow the instructions at the end of this project that tell you how to copy your project to your floppy diskette so you can take it home with you.
Rename the Form file and change it's Name and Text properties
With the form file (Form1.vb) selected in the Solution Explorer window, so that it's File properties are displayed in the Properties window, change the File Name property to frmProject13.vb (don't forget to include the .vb extension).
Now click on the form in the Designer window to display it's properties:
- Change the Name property to frmProject13
- Change the Text property to Project13-Loose Ends
Setting the Startup Object
Right-click on the Project13-Loose Ends project in your Solution Explorer window, click on the Properties item at the bottom of the context-menu. In the Project13-Loose Ends Property Pages dialog drop down the Startup object list and choose frmProject13 and click the OK button.
Adding some Buttons, a Tooltip control, and a ContextMenu control to the form
Use the illustration above, and add two Button controls to the form. Set these properties of the buttons as follows:
Button1 Property Value Name btnAddPics Text Add Pics Button2 Property Value Name btnDelPics Text Delete Pics
Now add a Tooltip (tooltip1) control and a ContextMenu (ContextMenu1) control to the form. Leave their default names unchanged. Set these additional properties of the form as follows:
frmProject13 Property Value Size.Width 450 ContextMenu ContextMenu1
Adding Tooltip Support
Tooltips are usually short popup messages that appear when you hold the mousepointer still over a control for 1 second. See the illustration below:
When you add Tooltip support to a control, you can specify how long the delay will be in milliseconds before the Tooltip originally appears with the InitialDelay property of the Tooltip control. We must also assign values to the AutoPopDelay, ReshowDelay, and ShowAlways properties of the Tooltip control. Here is what those 4 properties do:
- InitialDelay -- Determines the initial delay (in milliseconds) before the Tooltip message appears when the user first holds the mousepointer over the control.
- ReshowDelay -- Determines the length of time (in milliseconds) that it takes subsequent ToolTip messages to appear over a control where the initial display of a Tooltip message has already occurred.
- AutoPopDelay -- Determines the length of time that must pass before subsequent ToolTip messages appear as the mouse pointer moves from one control to another.
- ShowAlways -- Determines if Tooltip messages are displayed whether the form is active or not.
Add the following code to the Form's Load event procedure to initialize the Tooltip1 control and assign Tooltip messages to the Add Pics and Delete Pics buttons:
'Configure the behavior of Tooltips. The user must hold the mousepointer
' still over the control for 1 second before initially displaying a Tooltip.
ToolTip1.InitialDelay = 1000
'Reduce the delay to just half a second before redisplaying a Tooltip
' over a control where a Tooltip has already been displayed.
ToolTip1.ReshowDelay = 500
'So that Tooltip messages don't overlap, increase the delay to 3 seconds
' when displaying Tooltips while moving from one control to another.
ToolTip1.AutoPopDelay = 3000
'Force the ToolTip messages to be displayed whether the form is active or not.
ToolTip1.ShowAlways = True
We can then use the SetToolTip method of the Tooltip1 control to assign a Tooltip message to the different controls on our form. Add the following code to the Form's Load event procedure, below the above code:
'Add ToolTip messages to the Add and Delete buttons
ToolTip1.SetToolTip(btnAddPics, "Click to Add Pictures")
ToolTip1.SetToolTip(btnDelPics, "Click to Delete Pictures")Note: For design-time installed controls, like the Add Pics and Delete Pics buttons, we could have used their Tooltip on Tooltip1 properties to set the Tooltip message values in the properties window instead of setting them in code with the SetToolTip method as we have done above.
Now add the follow code at the end of the Form's Load event procedure to set the initial state of the Add Pics and Delete Pics buttons:
'Begin with the Add button enabled and the Delete button disabled
btnAddPics.Enabled = True
btnDelPics.Enabled = FalseTesting the program so far
Save the project now. Run the program. What happens when you hold the mouse pointer over the Add Pics and Delete Pics buttons?
Dynamically Creating Controls
To dynamically create controls at run-time requires you to do these 5 steps, in this order:
- Dimension reference variables for the controls you plan on dynamically constructing, like this:
Dim MyPicBox As PictureBox- Declare delegate procedures for the Event Procedures of the dynamically constructed controls that require code--Note: You don't have direct access at design-time to the Event Procedures of controls created at run-time.
- Add the code to construct the controls, like this:
MyPicBox = New PictureBox()- Add the code to add the newly constructed controls to the Form's Control collection, like this:
Me.Controls.Add(MyPicBox)- Add the code to link the Event Procedures of the newly created controls to the corresponding delegate procedures you previously created for them, like this:
AddHandler MyPicBox.Click, AddressOf MyPicBox_Click1) Dimension Reference variables for the controls you plan on dynamically constructing
Add the following code to the Declarations section:
'Create an array of PictureBox reference variables for the five
' PictureBoxes we will dynamically create.
Dim MyPicBox(5) As PictureBox
'Create a generic control reference variable (for use later)
Dim DeleteControl As Control
2) Declare delegate procedures for the Event Procedures of the dynamically constructed controls that require code.One problem with dynamically constructing controls is you do not have access to their Event Procedures at design-time (since the controls do not exist at design-time). Important: In order to use the Event Procedures of dynamically created controls, you must declare delegate procedures and assign them at run-time with the AddHandler command to those Event Procedures of the dynamically created controls that you want to use.
We will be dynamically constructing five PictureBoxes. In order to use their Click and MouseDown event procedures, we need to declare two delegate event procedures; One for Click and one for MouseDown. Type the following two delegate procedure declarations into the code window. Start on a blank line above the End Class statement at the bottom of the code window:
'Delegate Click event procedure for the MyPicBox collection of PictureBox controls
Private Sub MyPicBox_Click(ByVal sender As Object, _
ByVal e As EventArgs)
'sender is the MyPicBox control that raised the event
MessageBox.Show("MyPicBox #" & sender.Tag & " was clicked!")
End Sub
'Delegate MouseDown event procedure for the collection of PictureBox controls.
' This MouseDown procedure will allow the user to right-click on the dynamically
' created pictureboxes to display a context menu that gives them the option to
' to delete each picturebox.
Private Sub MyPicBox_MouseDown(ByVal sender As Object, _
ByVal e As MouseEventArgs)
'Display the context menu only if the user right-clicked
If e.Button = MouseButtons.Right Then
'sender is the MyPicBox control that raised the event. We use the
' generic DeleteControl control reference variable to actually
' delete the control if the user selects the Delete option on the
' context menu we are displaying 2 lines below.
DeleteControl = sender
'Create a point to specify where the context menu will popup
Dim p As New System.Drawing.Point(e.X - 4, e.Y - 4)
'Popup the context menu
ContextMenu1.Show(sender, p)
End If
End Sub
Save the project, but do not attempt to test run this project yet.
3, 4, and 5) Add code to construct the controls, Add them to the Form's Control collection, and Link them to their delegate proceduresAdd the following code to the Click event procedure of the Add Pics button (btnAddPics_Click):
Dim i As Integer
'Create 5 PictureBox controls and add them to the Form
For i = 0 To 4
'Create a new PictureBox
MyPicBox(i) = New PictureBox()
'Add the new PictureBox to the Form's controls collection
Me.Controls.Add(MyPicBox(i))
'Connect the Click and MouseDown event procedures of the new
' PictureBox to the Delegate Click and MouseDown procedures.
AddHandler MyPicBox(i).Click, AddressOf _
MyPicBox_Click
AddHandler MyPicBox(i).MouseDown, AddressOf _
MyPicBox_MouseDown
'Set initial values for several properties of the new PictureBox
MyPicBox(i).SizeMode = PictureBoxSizeMode.AutoSize
MyPicBox(i).Image = _
Image.FromFile("c:\shared\JPG Images\jeffrey.jpg")
'Line up the new PictureBoxes from left to right in a row
MyPicBox(i).Left = i * MyPicBox(i).Width
'Set the Tag property to the index value so we have an easy way of
' distinguishing one PictureBox from another by examining its
' Tag property at a later time.
MyPicBox(i).Tag = i
'Set the ToolTip message for each new PictureBox
ToolTip1.SetToolTip(MyPicBox(i), "PictureBox #" & i)
'Don't forget to make the new PictureBox visible
MyPicBox(i).Visible = True
Next i
'Disable the Add button and enable the Delete button
btnAddPics.Enabled = False
btnDelPics.Enabled = True
Testing the program so farSave the project now. Run the program. What happens when you click the Add Pics button? Once the PictureBoxes are created, do you see a different Tooltip message when you hold the mouse pointer over each picturebox?
Dynamically Destroying ControlsWhile dynamically creating controls requires 5 distinct steps, dynamically destroying them is much easier. Add the following code to the Click event procedure of the Del Pics button (btnDelPics_Click):
Dim i As Integer
'Before deleting anything, display the number of controls on the
' Form in the Output window .
Debug.WriteLine("Controls on Me before deletion: " & _
Me.Controls.Count)
'Technique #1 - Removes just PictureBoxes
'Construct a temporary picturebox so that we can compare it
' to the controls on the form to find the other pictureboxes.
Dim PicBox As New PictureBox()
'When deleting items from a collection, you must remove
' them from the end of the collect and work your way to the
' beginning. This is required because when you remove an
' item from a collection, the items below it move up to fill the
' gap left by the deleted item, i.e. if you delete item 2, item 3
' becomes item 2, item 4 becomes item 3, etc.
For i = Me.Controls.Count - 1 To 0 Step -1
'Write to the output window describing each control type
Debug.WriteLine("Control #" & i & " = " & _
Me.Controls.Item(i).GetType.ToString)
'The Object.ReferenceEquals method lets us compare
' two controls to see if they are equal.
If Object.ReferenceEquals(Me.Controls.Item(i).GetType, _
PicBox.GetType) Then
'If the control is a PictureBox, remove it from the Form
Me.Controls.Item(i).Dispose()
End If
Next i
'After removing the PictureBoxes, display the number of
' controls on the Form in the Output window.
Debug.WriteLine("Controls on Me after deletion: " & _
Me.Controls.Count)
'Once the PictureBoxes are deleted, enable the Add Pics
' and disable the Del Pics button.
btnAddPics.Enabled = True
btnDelPics.Enabled = False
The following two code examples are not being used but are shown as alternate techniques for dynamically deleting the picturebox controls (Do Not type the following):'Technique #2 - Removes ALL the controls from the Form
For i = 0 To Me.Controls.Count - 1
'The index value of the Item collection remains 0
' because when element 0 is removed the next
' element in the collection moves up to fill the gap.
Me.Controls.Item(0).Dispose()
Next i
'Technique #3 - Removes just the MyPicBox controls
For i = 0 To MyPicBox.Length - 1
'Be sure the control exists before trying to deleting it
If Not MyPicBox(i) Is Nothing Then
MyPicBox(i).Dispose()
End If
Next i
Testing the program so farSave the project now. Run the program. What happens when you click the Add Pics button to create the PictureBoxes, and then click the Del Pics button?
Creating a Context Menu
Set the ContextMenu property of the form to ContextMenu1, if you haven't already done so. Now use the following menu table and add these menu items to ContextMenu1, see the following illustration as well:
Menu Table
Text
Name
Delete this Control
mnuDelControl
(Insert Separator)
Cancel
mnuCancel
A ContextMenu does not appear at the top of a form like a regular MainMenu does at run-time. We must use the Show method of the ContextMenu to display it manually. Add the following code to the Click event procedure of the Delete this Control menu item (mnuDelControl_Click):'Delete the control referenced by the DeleteControl variable
DeleteControl.Dispose()
Add the following code to the Click event procedure of the Cancel menu item (mnuCancel_Click):'Clear any reference from the DeleteControl variable
DeleteControl = Nothing
Testing the program so farSave the project now. Run the program. Click the Add Pics button to create the PictureBoxes, and then right-click on one of the PictureBoxes. This executes the code in the MyPicBox_MouseDown procedure because we made it a delegate procedure to the MouseDown event procedure of the PictureBoxes--The code in the MyPicBox_MouseDown delegate procedure sets the DeleteControl control reference variable so that it references the picturebox that was right-clicked, i.e. DeleteControl = sender. When you select the Delete this Control menu item on the ContextMenu, the picturebox should be deleted.
Adding the ability to dynamically delete any controls
Now that we have our ContextMenu configured, we can add code to the MouseDown event procedures of any controls on our form (including the form itself) to allow the user to delete those controls. Add the following code to the MouseDown event procedure of the Add Pics button (btnAddPics_MouseDown)--this code will allow the user to delete the Add Pics button:
'Display the context menu only if the user right-clicked
If e.Button = MouseButtons.Right Then
DeleteControl = btnAddPics
'Create a point to designate where the context menu will popup
Dim p As New System.Drawing.Point(e.X - 4, e.Y - 4)
'Popup the ContextMenu
ContextMenu1.Show(btnAddPics, p)
End If
Add the following code to the MouseDown event procedure of the Del Pics button (btnDelPics_MouseDown)--this code will allow the user to delete the Del Pics button:'Display the context menu only if the user right-clicked
If e.Button = MouseButtons.Right Then
DeleteControl = btnDelPics
'Create a point to designate where the context menu will popup
Dim p As New System.Drawing.Point(e.X - 4, e.Y - 4)
'Popup the ContextMenu
ContextMenu1.Show(btnDelPics, p)
End If
Add the following code to the MouseDown event procedure of the Form (frmProject13_MouseDown)--this code will allow the user to delete the Form, which will terminate the program:'Display the context menu only if the user right-clicked
If e.Button = MouseButtons.Right Then
DeleteControl = Me
'Create a point to designate where the context menu will popup
Dim p As New System.Drawing.Point(e.X - 4, e.Y - 4)
'Popup the ContextMenu
ContextMenu1.Show(Me, p)
End If
Testing the program so farSave the project now. Run the program. What happens when you right-click the Add Pics button and select the Delete this Control item on the ContextMenu? Try right-clicking on the Del Pics button, and the Form itself. Also try clicking the Add Pics button and right-clicking on any of the dynamically created PictureBoxes. Important Note: If you right-click on a button that is disabled, the MouseDown event of the form behind it is raised. The user cannot raise the events of a control that is disabled.
Adding the ability to PrintWe will now use a PrintDocument control to let the user print text files. Look in the control toolbox, and add a PrintDocument control and OpenFileDialog control to the form. Do not change their default names. They will show up in the component tray, like this:
Adjust the locations of the Add Pics and Del Pics buttons and add a new button to the form, as shown in the illustration below:
Set the Name property of the new button to btnPrintTextFile, and set it's Text property to Print a Text File.Add the following code to the Declarations section:
'Declare a Font reference variable so that we can specify the
' what font the text document is printed in.
Dim PrintFont As System.Drawing.Font
'Declare a StreamReader reference variable so that we can
' construct a StreamReader to read the text file for printing.
Dim StreamToPrint As System.IO.StreamReaderWe will use the OpenFileDialog control to let the user browse for a text file they want to print. Add the following code to the Click event procedure of the Print a Text File button (btnPrintTextFile_Click):
'Set several properties of the OpenFileDialog before Showing it
OpenFileDialog1.Title = "Select a Document to Print"
'List files with a .txt extension only
OpenFileDialog1.Filter = "Text Files (*.txt) | *.txt"
'If the user types a filename the .txt extension is added
' automatically if they don't include it.
OpenFileDialog1.AddExtension = True
'Clear the FileName property of OpenFileDialog1, so any
' previously selected file is not displayed.
OpenFileDialog1.FileName = ""
'Show the OpenFileDialog
OpenFileDialog1.ShowDialog()
'Make sure the user selected a file to print
If OpenFileDialog1.FileName <> "" Then
Try
'Create the Font we will use to print the text file
PrintFont = New System.Drawing.Font("Arial", 10)
'Create the StreamReader we will use to read the text file
StreamToPrint = New _
System.IO.StreamReader(OpenFileDialog1.FileName)
'Initiate the printing process. This raises the PrintPage event
' procedure of the PrintDocument1 object, which is where
' we actually read the text file and write it out to the printer,
' line by line.
PrintDocument1.Print()
'Close the FileStream and destroy it once printing is complete
StreamToPrint.Close()
StreamToPrint = Nothing
Catch ex As Exception
MessageBox.Show("Error printing the file - " & ex.Message)
End Try
End IfUsing the PrintPage event procedure of the PrintDocument object to print the file
To actually print the text file, we need to add code to the PrintPage event procedure of PrintDocument1. This event is raised when we executed the PrintDocument1.Print method in the code above. Add the following code to the PrintPage event procedure of PrintDocument1 (PrintDocument1_PrintPage):
Dim LinesPerPage As Single = 0
Dim LinePosition As Single = 0
Dim LineCount As Integer = 0
Dim sLine As String
'To calculate the number of lines per page use e.MarginBounds
' divided by the PrintFont height.
LinesPerPage = _
e.MarginBounds.Height / PrintFont.GetHeight(e.Graphics)
'Now read a line from the file and print it. Keep going until a full
' page is printed. If there are still more lines to print, set the
' e.HasMorePages property to True, and this PrintPage event will
' be raised again. Keep going until the file is completely printed.
sLine = StreamToPrint.ReadLine()
'Keep printing lines until the end of a page is reached or there are
' no more lines to print, whichever comes first.
While (LineCount < LinesPerPage And sLine <> Nothing)
'Determine the line position on the printed page
LinePosition = e.MarginBounds.Top + _
(LineCount * PrintFont.GetHeight(e.Graphics))
'Print the line to the default printer and increment the LineCount
e.Graphics.DrawString(sLine, PrintFont, Brushes.Black, _
e.MarginBounds.Left, LinePosition, New StringFormat)
LineCount += 1
'If we're not finished printing a page, read the next line from the file
If (LineCount < LinesPerPage) Then
sLine = StreamToPrint.ReadLine()
End If
End While
'Once we are finished printing a page, if we have more lines to print,
' then set the e.HasMorePages property to True, which will force
' this PrintPage event to be raised again.
If (sLine <> Nothing) Then
e.HasMorePages = True
Else
e.HasMorePages = False
End If
Testing the program so farSave the project now. Run the program. Use the Print a Text File button and print a small text file.
Printing other types of Documents
The above code works fine for printing text files. But what if you wanted to print Word documents, or Excel spreadsheets from within your VB.NET applications? We can use Microsoft Word to print Word documents for us. For example, to print Microsoft Word documents instead of text files, the code in the Click event procedure of the Print a Text File button would be replaced with the following code. Note: You need to set a reference to the Microsoft Word 10.0 Object Library--like we did back in the MyEditor project--before using the following code:
'Set several properties of the OpenFileDialog before Showing it
OpenFileDialog1.Title = "Select a Word Document to Print"
'List files with a .doc extension only
OpenFileDialog1.Filter = "Word Documents (*.doc) | *.doc"
'If the user types a filename the .doc extension is added
' automatically if they don't include it.
OpenFileDialog1.AddExtension = True
'Clear the FileName property of OpenFileDialog1, so any
' previously selected file is not displayed.
OpenFileDialog1.FileName = ""
'Show the OpenFileDialog
OpenFileDialog1.ShowDialog()
'Make sure the user selected a file to print
If OpenFileDialog1.FileName <> "" Then
Try
'Declare a Word application reference variable and
' construct an instance of Microsoft Word.
Dim WordObj As Word.ApplicationClass
WordObj = New Word.ApplicationClass
'Open the Word document the user wants to print
WordObj.Documents.Open(OpenFileDialog1.FileName)
'Print the document to the default printer
WordObj.PrintOut()
'Close the document without saving it
With WordObj.Documents
.Close(Word.WdSaveOptions.wdDoNotSaveChanges)
End With
'Quit Microsoft Word
WordObj.Quit()
'Set the WordObj reference variable to Nothing so
' that our instance of Word can terminate.
WordObj = Nothing
Catch ex As Exception
MessageBox.Show("An error occurred printing the file - " _
& ex.Message)
End Try
End If
Note: We don't even need a PrintDocument control if we use the above method to print Word documents.We could print Excel spreadsheets from within our VB.NET applications in a similar way.
To copy a Project folder from your Solution on the Hard Drive to a floppy diskette, follow these steps:
- Exit Visual Basic .NET and insert the floppy diskette, that you want to copy the Project folder to, into drive A:
- Select the My Documents item on the Start Menu to open the My Documents folder.
- In the My Documents folder, double-click the Visual Studio Projects folder to open it.
- Double-click on your Solution folder to open it (it should have your name).
- Open the Project folder that you want to copy, by double-clicking on it.
Deleting the Obj and Bin folders from inside the Project folder before copying it.
- Inside the Project folder, delete the Obj and Bin folders--these folders are created automatically when you open a project. You do not need to copy them, or their contents, to your floppy diskette.
- Hit the Backspace key once--or click the Back button on the toolbar. This moves you from inside the Project folder to back inside your Solution folder.
- Right-click on the Project folder and selected: 3 1/2" Floppy A: on the Send To fly-out menu. This copies the Project folder to your floppy diskette.