Advanced Visual Basic .NET - Project 13

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:

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:

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 = False

Testing 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:

  1. Dimension reference variables for the controls you plan on dynamically constructing, like this:
        Dim MyPicBox As PictureBox
  2. 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.
  3. Add the code to construct the controls, like this:
        MyPicBox = New PictureBox()
  4. Add the code to add the newly constructed controls to the Form's Control collection, like this:
        Me.Controls.Add(MyPicBox)
  5. 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_Click

1) 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 procedures

Add 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).ClickAddressOf  _
                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 far

Save 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 Controls

While 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 far

Save 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 far

Save 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 far

Save 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 Print

We 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.StreamReader

We 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 If

Using 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 far

Save 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:

  1. Exit Visual Basic .NET and insert the floppy diskette, that you want to copy the Project folder to, into drive A:
  2. Select the My Documents item on the Start Menu to open the My Documents folder.
  3. In the My Documents folder, double-click the Visual Studio Projects folder to open it.
  4. Double-click on your Solution folder to open it (it should have your name).
  5. 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.

  1. 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.
  2. 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.
  3. 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.