Skip to content

5. Graphical Interfaces with VB.NET and VS.NET

Here, we aim to demonstrate how to build graphical user interfaces with VB.NET. First, we will examine the core classes of the .NET platform that enable us to build a graphical user interface. Initially, we will not use any automatic generation tools. Then we will use Visual Studio.NET (VS.NET), a Microsoft development tool that facilitates the development of applications using .NET languages, particularly the creation of graphical user interfaces. The version of VS.NET used is the English version.

5.1. The Basics of Graphical User Interfaces

5.1.1. A simple window

Consider the following code:


' options
Option Strict On
Option Explicit On 

' namespaces
Imports System
Imports System.Drawing
Imports System.Windows.Forms

' the Form class
Public Class Form1
    Inherits Form

    ' the constructor
    Public Sub New()
        ' window title
        Me.Text = "My first form"
        ' window dimensions
        Me.Size = New System.Drawing.Size(300, 100)
    End Sub

    ' test function
    Public Shared Sub Main(ByVal args() As String)
        ' display the form
        Application.Run(New Form1)
    End Sub
End Class

The previous code is compiled and then executed

dos>vbc /r:system.dll /r:system.drawing.dll /r:system.windows.forms.dll frm1.vb

dos>frm1

Execution displays the following window:

Image

A graphical user interface generally derives from the base class System.Windows.Forms.Form:

Public Class Form1
    Inherits Form

The base Form class defines a basic window with close, maximize/minimize buttons, adjustable size, etc., and handles events on these graphical objects. Here we specialize the base class by setting its title, width (300), and height (100). This is done in its constructor:


    Public Sub New()
        ' window title
        Me.Text = "My first form"
        ' window dimensions
        Me.Size = New System.Drawing.Size(300, 100)
    End Sub

The window title is set by the Text property and the dimensions by the Size property. Size is defined in the System.Drawing namespace and is a structure. The Main procedure launches the graphical application as follows:


        Application.Run(New Form1)

A Form1-type form is created and displayed, then the application listens for events that occur on the form (clicks, mouse movements, etc.) and executes those that the form handles. Here, our form does not handle any events other than those handled by the base Form class (clicks on close, maximize/minimize buttons, window resizing, window movement, etc.).

5.1.2. A Form with a Button

Now let’s add a button to our window:


' options
Option Strict On
Option Explicit On 

' namespaces
Imports System
Imports System.Drawing
Imports System.Windows.Forms

' the Form class
Public Class Form1
    Inherits Form

    ' attributes
    Private cmdTest As Button

    ' the constructor
    Public Sub New()
        ' the title
        Me.Text = "My first form"
        ' dimensions
        Me.Size = New System.Drawing.Size(300, 100)
        ' a button
        ' creation
        Me.cmdTest = New Button
        ' position
        cmdTest.Location = New System.Drawing.Point(110, 20)
        ' size
        cmdTest.Size = New System.Drawing.Size(80, 30)
        ' label
        cmdTest.Text = "Test"
        ' event handler
        AddHandler cmdTest.Click, AddressOf cmdTest_Click
        ' Add button to form
        Me.Controls.Add(cmdTest)
    End Sub

    ' event handler
    Private Sub cmdTest_Click(ByVal sender As Object, ByVal evt As EventArgs)
        ' The button was clicked - display a message
        MessageBox.Show("Button Click", "Button Click", MessageBoxButtons.OK, MessageBoxIcon.Information)
    End Sub

    ' test function
    Public Shared Sub Main(ByVal args() As String)
        ' Display the form
        Application.Run(New Form1)
    End Sub
End Class

We have added a button to the form:


        ' a button
        ' creation
        Me.cmdTest = New Button
        ' position
        cmdTest.Location = New System.Drawing.Point(110, 20)
        ' size
        cmdTest.Size = New System.Drawing.Size(80, 30)
        ' label
        cmdTest.Text = "Test"
        ' event handler
        AddHandler cmdTest.Click, AddressOf cmdTest_Click
        ' Add button to form
        Me.Controls.Add(cmdTest)

The Location property sets the coordinates (110,20) of the button's top-left corner using a Point structure. The button's width and height are set to (80,30) using a Size structure. The button's Text property sets the button's label. The Button class has a Click event defined as follows:

Public Event Click As EventHandler

where EventHandler is a "delegate" function with the following signature:

Public Delegate Sub EventHandler(ByVal sender As Object, ByVal e As EventArgs)

This means that the handler for the [Click] event on the button must have the signature of the [EventHandler] delegate. Here, when the cmdTest button is clicked, the cmdTest_Click method will be called. This is defined as follows, in accordance with the previous EventHandler model:


    ' event handler
    Private Sub cmdTest_Click(ByVal sender As Object, ByVal evt As EventArgs)
        ' The button was clicked - we say
        MessageBox.Show("Button Clicked", "Button Clicked", MessageBoxButtons.OK, MessageBoxIcon.Information)
    End Sub

We simply display a message:

Image

The class is compiled and executed:

dos>vbc /r:system.dll /r:system.drawing.dll /r:system.windows.forms.dll frm2.vb
Microsoft (R) Visual Basic .NET Compiler version 7.10.3052.4

dos>frm2

The MessageBox class is used to display messages in a window. Here, we have used the constructor

Overloads Public Shared Function Show(ByVal owner As IWin32Window, ByVal text As String, ByVal caption As String, ByVal buttons As MessageBoxButtons, ByVal icon As MessageBoxIcon) As DialogResult
text
the message to display
caption
the window title
buttons
the buttons in the window
icon
the icon in the window

The buttons parameter can take values from the following constants:

constant
buttons
   AbortRetryIgnore
  OK
    OKCancel
    RetryCancel
    YesNo
    YesNoCancel

The icon parameter can take values from the following constants:

Asterisk
Error
same as above Stop
Exclamation
same as Warning
Hand
Information
same as Asterisk
None
Question
Stop
Same as Hand
Warning

 

The Show method is a static method that returns a result of type System.Windows.Forms.DialogResult, which is an enumeration:


public enum System.Windows.Forms.DialogResult 
{
    Abort = 0x00000003, 
    Cancel = 0x00000002, 
    Ignore = 0x00000005, 
    No = 0x00000007, 
    None = 0x00000000, 
    OK = 0x00000001, 
    Retry = 0x00000004, 
    Yes = 0x00000006, 
}

To determine which button the user clicked to close the MessageBox window, we write:

dim res as DialogResult = MessageBox.Show(..)
if res = DialogResult.Yes then
 ' they clicked the Yes button
...
end if

5.2. Building a graphical user interface with Visual Studio.NET

We will revisit some of the examples seen previously, this time building them with Visual Studio.NET.

5.2.1. Initial project creation

  1. Launch VS.NET and select File/New/Project

Image

  1. Specify your project’s characteristics
    1. Select the type of project you want to build; here, a VB.NET project (1)
    2. Select the type of application you want to build; here, a Windows application (2)
    3. Specify the folder where you want to place the project subfolder (3)
    4. Enter the project name (4). This will also be the name of the folder that will contain the project files
    5. The name of this folder is displayed in (5)
  1. A number of folders and files are then created under the i4 folder:
subfolders of the project1 folder

Of these files, only one is relevant: the form1.cs file, which is the source file associated with the form created by VS.NET. We’ll come back to this later.

5.2.2. The VS.NET interface windows

The VS.NET interface now displays certain elements of our i4 project:

We have a graphical user interface design window:

By dragging controls from the toolbar (Toolbox 2) and dropping them onto the window pane (1), we can build a graphical user interface. If we hover the mouse over the "Toolbox," it expands to reveal a number of controls:

Image

For now, we won’t use any of them. Still on the VS.NET screen, we find the “Explorer Solution” window:

Image

Initially, we won’t use this window much. It displays all the files that make up the project. We’re only interested in one of them: our program’s source file, in this case Form1.vb. Right-clicking on Form1.vb brings up a menu that lets you access either the source code for our GUI (View Code) or the GUI itself (Form Designer):

Image

You can access both of these directly from the "Solution Explorer" window:

 

The open windows "stack" in the main design window:

Image

Here, Form1.vb[Design] refers to the design window and Form1.vb to the code window. Simply click on one of the tabs to switch between the two windows. Another important window displayed on the VS.NET screen is the Properties window:

Image

The properties displayed in the window are those of the control currently selected in the graphical design window. You can access different project windows using the View menu:

Image

It includes the main windows just described, along with their keyboard shortcuts.

5.2.3. Running a Project

Even though we haven’t written any code, we have an executable project. Press F5 or Debug/Start to run it. We get the following window:

Image

This window can be maximized, minimized, resized, and closed.

5.2.4. The code generated by VS.NET

Let’s look at the code (View/Code) for our application:


Public Class Form1
    Inherits System.Windows.Forms.Form

#Region "Code generated by the Windows Form Designer"

    Public Sub New()
        MyBase.New()

        'This call is required by the Windows Forms Designer.
        InitializeComponent()

        'Add any initialization after the InitializeComponent() call

    End Sub

    'The form's overridden Dispose method to clean up the component list.
    Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
        If disposing Then
            If Not (components Is Nothing) Then
                components.Dispose()
            End If
        End If
        MyBase.Dispose(disposing)
    End Sub

    'Required by the Windows Forms Designer
    Private components As System.ComponentModel.IContainer

    'NOTE: The following procedure is required by the Windows Forms Designer
    'It can be modified using the Windows Forms Designer.  
    'Do not modify it using the code editor.
    <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
        components = New System.ComponentModel.Container()
        Me.Text = "Form2"
    End Sub

#End Region

End Class

A graphical user interface derives from the base class System.Windows.Forms.Form:

Public Class Form1
    Inherits System.Windows.Forms.Form

The Form base class defines a basic window with close and maximize/minimize buttons, adjustable size, and more, and handles events on these graphical objects. The form's constructor uses an InitializeComponent method in which the form's controls are created and initialized.


    Public Sub New()
        MyBase.New()

        'This call is required by the Windows Form Designer.
        InitializeComponent()

        'Add any initialization after the InitializeComponent() call
    End Sub

Any other work to be done in the constructor can be done after the InitializeComponent call. The InitializeComponent method


Private Sub InitializeComponent()
        '
        'Form1
        '
        Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
        Me.ClientSize = New System.Drawing.Size(292, 53)
        Me.Name = "Form1"
        Me.Text = "Form1"
    End Sub

sets the title of the "Form1" window, its width (292), and its height (53). The window title is set by the Text property and the dimensions by the Size property. Size is defined in the System.Drawing namespace and is a structure. To run this application, we need to define the project’s main module. To do this, we use the [Projects/Properties] option:

Image

In [Startup Object], we specify [Form1], which is the form we just created. To run the application, we use the [Debug/Start] option:

Image

5.2.5. Compiling in a Command Prompt Window

Now, let’s try compiling and running this application in a DOS window:

dos>dir form1.vb
03/14/2004  11:53               514 Form1.vb

dos>vbc /r:system.dll /r:system.windows.forms.dll form1.vb

vbc: error BC30420: 'Sub Main' not found in 'Form1'.

The compiler indicates that it cannot find the [Main] procedure. Indeed, VS.NET did not generate it. However, we have already encountered it in the previous examples. It has the following form:


    Shared Sub Main()
        ' we launch the app
        Application.Run(New Form1) ' where Form1 is the form
    End Sub

Let’s add the previous code to [Form1.vb] and recompile:

dos>vbc /r:system.dll /r:system.windows.forms.dll form2.vb
Microsoft (R) Visual Basic .NET Compiler version 7.10.3052.4

Form2.vb(41): error BC30451: The name 'Application' is not declared.

        Application.Run(New Form2)
        ~~~~~~~~~~~

This time, the name [Application] is unknown. This simply means that we haven't imported its namespace [System.Windows.Forms]. Let's add the following statement:

Imports System.Windows.Forms

then recompile:

dos>vbc /r:system.dll /r:system.windows.forms.dll form1.vb

This time, it works. Let's run it:

dos>dir
03/14/2004  11:53               514 Form1.vb
03/14/2004  12:15             3,584 Form1.exe

dos>form1

Image

A Form1-type form is created and displayed. You can avoid adding the [Main] procedure by using the compiler’s /m option, which allows you to specify the class to execute if it inherits from System.Windows.Forms:

dos>vbc /r:system.dll /r:system.windows.forms.dll /r:system.drawing.dll /m:form2 form2.vb

The /m:form2 option specifies that the class to be executed is the class named [form2].

5.2.6. Event handling

Once the form is displayed, the application listens for events that occur on the form (clicks, mouse movements, etc.) and executes those that the form handles. Here, our form does not handle any events other than those handled by the base Form class (clicks on close buttons, maximize/minimize buttons, window resizing, window movement, etc.). The generated form uses a components attribute that is not used anywhere. The Dispose method is also unnecessary here. The same applies to certain namespaces (Collections, ComponentModel, Data) that are used and the one defined for the projet1 project. Therefore, in this example, the code can be simplified to the following:


Imports System
Imports System.Drawing
Imports System.Windows.Forms

Public Class Form1
    Inherits System.Windows.Forms.Form

    ' constructor
    Public Sub New()
        ' Create the form with its components
        InitializeComponent()
    End Sub

    Private Sub InitializeComponent()
        ' window size
        Me.Size = New System.Drawing.Size(300, 300)
        ' window title
        Me.Text = "Form1"
    End Sub

    Shared Sub Main()
        ' launch the app
        Application.Run(New Form1)
    End Sub
End Class

5.2.7. Conclusion

We will now accept the code generated by VS.NET as is and simply add our own code, specifically to handle events related to the various controls on the form.

5.3. Window with input field, button, and label

5.3.1. Graphical Design

In the previous example, we did not place any components in the window. We will start a new project called interface2. To do this, we will follow the procedure described earlier to create a project:

Image

Now let’s build a window with a button, a label, and an input field:

The fields are as follows:

No.
name
Type
role
1
lblInput
Label
a label
2
txtInput
TextBox
an input field
3
btnDisplay
Button
to display the contents of the txtSaisie text box in a dialog box

You can proceed as follows to build this window: right-click inside the window outside of any component and select the Properties option to access the window's properties:

Image

The Properties window then appears on the right:

Image

Some of these properties are worth noting:

BackColor
to set the window's background color
ForeColor
to set the color of the graphics or text in the window
Menu
to associate a menu with the window
Text
to give the window a title
FormBorderStyle
to set the window type
Font
to set the font for text in the window
Name
to set the window name

Here, we set the Text and Name properties:

Text
Zippers & buttons - 1
Name
frmInputButtons

Using the "Toolbox" bar

  • select the components you need
  • drop them onto the window and set their correct dimensions
Once you have selected the component from the "toolbox," use the
"Esc" key to hide the toolbar, then drop
and resize the component. Do this for the three
you need: Label, TextBox, Button. To align and
size the components correctly, use the Format menu:
The formatting process works as follows:
  1. select the various components to be formatted together (hold down the Ctrl key)
  2. select the desired formatting option
The Align option allows you to align components
The Make Same Size option ensures that
components have the same height or the same
width:
The Horizontal Spacing option, for example,
to align components horizontally with
equal spacing between them. The same applies
for the Vertical Spacing option to align vertically.
The Center in Form option allows you to center
a component horizontally or
vertically within the window:
Once the components are correctly
placed in the window, set their properties.
To do this, right-click on the component and
select the Properties option:
  • Label 1
Select the component to open
open its Properties window. In this window,
modify the following properties:
name: lblSaisie, text: Saisie
  • Input field 2 (TextBox)
Select the component to open its
properties window. In this window, modify
the following properties:
name: txtSaisie, text: leave blank
  • Button 3 (Button):
name: cmdAfficher, text: Display
  • the window itself: name: frmSaisies&Boutons,
text: Entries & Buttons - 1
We can run (Ctrl-F5) our project
to get a first look at the window
in action:

Close the window. We still need to write the procedure associated with a click on the Display button.

5.3.2. Handling form events

Let’s look at the code generated by the visual designer:


...

Public Class frmSaisiesBoutons
  Inherits System.Windows.Forms.Form

  ' components
    Private components As System.ComponentModel.Container = Nothing
    Friend WithEvents btnDisplay As System.Windows.Forms.Button
    Friend WithEvents lblsaisie As System.Windows.Forms.Label
    Friend WithEvents txtsaisie As System.Windows.Forms.TextBox

  ' constructor
  Public Sub New()
    InitializeComponent()
  End Sub

...

  ' initialize components
    Private Sub InitializeComponent()
...
    End Sub
End Class

First, note the specific declaration of the components:

  • the Friend keyword indicates that the component is visible to all classes in the project
  • the WithEvents keyword indicates that the component generates events. We will now look at how to handle these events

Display the form’s code window (View/Code or F7):

The window above displays two drop-down lists (1) and (2). List (1) is the list of form components:

Image

List (2) shows the events associated with the component selected in (1):

Image

One of the events associated with the component is displayed in bold (here, Click). This is the component’s default event. You can access the handler for this specific event by double-clicking the component in the design window. VB.NET then automatically generates the skeleton of the event handler in the code window and positions the cursor on it. To access the handlers for the other events, go to the code window, select the component from the list (1), and select the event from (2). VB.NET then generates the skeleton of the event handler or positions the cursor on it if it was already generated:


    Private Sub btnAfficher_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAfficher.Click
...
    End Sub

By default, VB.NET names the event handler for event E of component C. You can change this name if you wish. However, this is not recommended. VB developers generally keep the name generated by VB, which ensures naming consistency across all VB programs. The association of the btnAfficher_Click procedure with the Click event of the btnAfficher component is not done via the procedure name but via the handles keyword:


Handles btnAfficher.Click

In the code above, the handles keyword specifies that the procedure handles the Click event of the btnAfficher component. The event handler has two parameters:

sender
the object that triggered the event (in this case, the button)
e
an EventArgs object that details the event that occurred

We won’t use any of these parameters here. All that’s left is to complete the code. Here, we want to display a dialog box containing the contents of the txtSaisie field:


    Private Sub btnAfficher_Click(ByVal sender As Object, ByVal e As System.EventArgs)
        ' display the text entered in the TxtSaisie text box
        MessageBox.Show("text entered= " + txtsaisie.Text, "Input Verification", MessageBoxButtons.OK, MessageBoxIcon.Information)
    End Sub

When the application is run, the following occurs:

Image

5.3.3. Another method for handling events

For the btnAfficher button, VS.NET generated the following code:


Private Sub InitializeComponent()
        ...
        '
        'btnDisplay
        '
        Me.btnDisplay.Location = New System.Drawing.Point(102, 48)
        Me.btnDisplay.Name = "btnDisplay"
        Me.btnDisplay.Size = New System.Drawing.Size(88, 24)
        Me.btnDisplay.TabIndex = 2
        Me.btnDisplay.Text = "Display"
...
    End Sub

...

    ' event handler for click on the cmdShow button
    Private Sub btnAfficher_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAfficher.Click
...
    End Sub

We can associate the btnAfficher_Click procedure with the Click event of the btnAfficher button in another way:


Private Sub InitializeComponent()
        ...
        '
        'btnAfficher
        '
        Me.btnDisplay.Location = New System.Drawing.Point(102, 48)
        Me.btnDisplay.Name = "btnDisplay"
        Me.btnDisplay.Size = New System.Drawing.Size(88, 24)
        Me.btnDisplay.TabIndex = 2
        Me.btnDisplay.Text = "Display"
        AddHandler btnDisplay.Click, AddressOf btnDisplay_Click
...
    End Sub

...

    ' event handler for click on cmdShow button
    Private Sub btnDisplay_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
...
    End Sub

The btnAfficher_Click procedure has lost the Handles keyword, thereby losing its association with the btnAfficher.Click event. This association is now made using the AddHandler keyword:


        AddHandler btnAfficher.Click, AddressOf btnAfficher_Click

The code above, which will be placed in the form's InitializeComponent procedure, associates the procedure named btnAfficher_Click with the btnAfficher.Click event. Furthermore, the btnAfficher component no longer requires the WithEvents keyword:


    Friend btnAfficher As System.Windows.Forms.Button

What is the difference between the two methods?

  • The handles keyword only allows an event to be associated with a procedure at design time. The designer knows in advance that a procedure P must handle events E1, E2, ... and writes the code

    Private Sub btnAfficher_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) handles E1, E2, ..., En

It is indeed possible for a procedure to handle multiple events.

  • The addhandler keyword allows an event to be associated with a procedure at runtime. This is useful in a producer-consumer event framework. An object produces a specific event that may be of interest to other objects. These objects subscribe to the producer to receive the event (a temperature exceeding a critical threshold, for example). During the application’s execution, the event producer will be required to execute various instructions:
Addhandler E, AddressOf P1
Addhandler E, AddressOf P2
...
Addhandler E, AddressOf Pn

where E is the event produced by the producer and P1 is one of the procedures belonging to the various objects consuming this event. We will have the opportunity to revisit a producer-consumer event application in a later chapter.

5.3.4. Conclusion

From the two projects studied, we can conclude that once the graphical user interface is built with VS.NET, the developer’s task is to write the event handlers for the events they wish to manage within that interface. From now on, we will present only the code for these handlers.

5.4. Some Useful Components

We will now present various applications that utilize the most common components to explore their main methods and properties. For each application, we will present the graphical user interface and the relevant code, particularly the event handlers.

5.4.1. Form

We’ll start by introducing the essential component: the form onto which components are placed. We’ve already covered some of its basic properties. Here, we’ll focus on a few important form events.

 
The form is loading
Closing
The form is closing
Closed
The form is closed

The Load event occurs before the form is even displayed. The Closing event occurs when the form is being closed. This closing can still be stopped programmatically. We create a form named Form1 without any components:

Image

We handle the three previous events:


    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
        ' initial form load
        MessageBox.Show("Evt Load", "Load")
    End Sub

    Private Sub Form1_Closing(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles MyBase.Closing
        ' the form is closing
        MessageBox.Show("Evt Closing", "Closing")
        ' asking for confirmation
        Dim response As DialogResult = MessageBox.Show("Do you really want to exit the application?", "Closing", MessageBoxButtons.YesNo, MessageBoxIcon.Question)
        If response = DialogResult.No Then
            e.Cancel = True
        End If
    End Sub

    Private Sub Form1_Closed(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Closed
        ' The form is closing
        MessageBox.Show("Evt Closed", "Closed")
    End Sub
We use the MessageBox function to be notified of the various
events. The Closing event occurs when the user
closes the window.
We then ask them if they really want to exit the application:
If they answer No, we set the Cancel property of the
CancelEventArgs that the method received as a parameter.
If we set this property to False, the
of the window is canceled; otherwise, it proceeds:

5.4.2. Label controls and TextBox controls

We have already encountered these two components. Label is a text component and TextBox is an input field component. Their main property is Text, which refers to either the content of the input field or the label text. This property is read/write. The event typically used for TextBox is TextChanged, which signals that the user has modified the input field. Here is an example that uses the TextChanged event to track changes in an input field:

No.
type
name
role
1
TextBox
txtInput
input field
2
Label
lblControl
displays the text from 1 in real time
3
Button
cmdClear
to clear fields 1 and 2
4
Button
cmdExit
to exit the application

The relevant code for this application is the event handlers:


  ' Click on the Exit button
    Private Sub cmdQuitter_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles cmdQuitter.Click
        ' Click on the Exit button - exit the application
        Application.Exit()
    End Sub

    ' txtSaisie field changed
    Private Sub txtSaisie_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles txtSaisie.TextChanged
        ' The TextBox content has changed - copy it to the Label lblControle
        lblControl.Text = txtInput.Text
    End Sub

    ' Click on the Clear button
    Private Sub cmdClear_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles cmdEffacer.Click
        ' Clear the contents of the text box
        txtInput.Text = ""
    End Sub

    ' a key has been pressed
    Private Sub txtInput_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) _
    Handles txtSaisie.KeyPress
        ' check which key was pressed
        Dim key As Char = e.KeyChar
        If key = ControlChars.Cr Then
            MessageBox.Show(txtSaisie.Text, "Control", MessageBoxButtons.OK, MessageBoxIcon.Information)
            e.Handled = True
        End If
    End Sub

Note how the application is terminated in the cmdQuitter_Click procedure: Application.Exit(). The following example uses a multi-line TextBox:

The list of controls is as follows:

No.
Type
name
role
1
TextBox
txtMultiLines
multi-line input field
2
TextBox
txtAdd
single-line input field
3
Button
btnAdd
Subtract 2 from 1

To make a TextBox multi-line, set the following control properties:

 
to allow multiple lines of text
ScrollBars=(None, Horizontal, Vertical, Both)
to specify whether the control should have scroll bars (Horizontal, Vertical, Both) or not (None)
AcceptReturn=(True, False)
if set to true, the Enter key will move to the next line
AcceptTab=(True, False)
if set to true, the Tab key will insert a tab in the text

The relevant code is the one that handles the click on the [Add] button and the one that handles changes to the [txtAdd] input field:


    ' btnAdd_Click
    Private Sub btnAdd_Click1(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btnAdd.Click
        ' Append the contents of txtAjout to those of txtMultilignes
        txtMultilines.Text &= txtAdd.Text
        txtAdd.Text = ""
    End Sub

    ' txtAjout_TextChanged event
    Private Sub txtAdd_TextChanged1(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles txtAdd.TextChanged
        ' Set the state of the Add button
        btnAdd.Enabled = txtAdd.Text.Trim() <> ""
    End Sub

5.4.3. ComboBox drop-down lists

A ComboBox component is a drop-down list combined with an input field: the user can either select an item from (2) or type text into (1). There are three types of ComboBox defined by the Style property:

 
non-dropdown list with edit box
DropDown
drop-down list with edit box
DropDownList
drop-down list without an edit box

By default, the type of a ComboBox is DropDown. To learn more about the ComboBox class, type ComboBox in the Help index (Help/Index). The ComboBox class has a single constructor:

Public Sub New()
creates an empty ComboBox object

The items in the ComboBox are available in the Items property:

Public ReadOnly Property Items As ComboBox.ObjectCollection

This is an indexed property, where Items(i) refers to the i-th item in the Combo. Let C be a Combo and C.Items its list of items. We have the following properties:

 
number of items in the ComboBox
C.Items(i)
element i of the ComboBox
C.Add(object o)
adds the object o as the last element of the combo
C.AddRange(object() objects)
adds an array of objects to the end of the combo
C.Insert(int i, object o)
adds the object o at position i in the combo
C.RemoveAt(int i)
removes element i from the combo box
C.Remove(object o)
removes object o from the combo box
C.Clear()
clears all items from the combo box
C.IndexOf(object o)
returns the position i of object o in the combo box

It may seem surprising that a combo box can contain objects when it usually contains strings. Visually, this is indeed the case. If a ComboBox contains an object obj, it displays the string obj.ToString(). Recall that every object has a ToString method inherited from the object class, which returns a string that "represents" the object. The selected item in the combo box C is C.SelectedItem or C.Items(C.SelectedIndex), where C.SelectedIndex is the index of the selected item, starting from zero for the first item.

When an item is selected from the drop-down list, the SelectedIndexChanged event is triggered, which can then be used to detect a change in the combo box's selection. In the following application, we use this event to display the item that has been selected from the list.

Image

We are only showing the relevant code for the window. In the form's constructor, we populate the combo box:


  Public Sub New()
    ' create form
    InitializeComponent()
    ' populate combo
    cmbNumbers.Items.AddRange(New String() {"zero", "one", "two", "three", "four"})
        ' Select the first item in the list
    cmbNumbers.SelectedIndex = 0
  End Sub

We handle the combo's SelectedIndexChanged event, which signals that a new item has been selected:


    Private Sub cmbNumbers_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles cmbNumbers.SelectedIndexChanged
        ' The selected item has changed—we display it
        MessageBox.Show("Selected item: (" & cmbNumbers.SelectedItem.ToString & "," & cmbNumbers.SelectedIndex & ")", "Combo", MessageBoxButtons.OK, MessageBoxIcon.Information)
    End Sub

5.4.4. ListBox component

We propose to build the following interface:

The components of this window are as follows:

No.
Type
name
role/properties
0
Form
Form1
form - BorderStyle=FixedSingle
1
TextBox
txtInput
input field
2
Button
btnAdd
button to add the contents of input field 1 to list 3
3
ListBox
listBox1
List 1
4
ListBox
listBox2
list 2
5
Button
btn1TO2
transfers the selected items from list 1 to list 2
6
Button
cmd2T0
does the opposite
7
Button
btnClear1
clears list 1
8
Button
btnClear2
clears list 2
  • The user types text into field 1. They add it to list 1 using the Add button (2). The input field (1) is then cleared, and the user can add a new item.
  • They can transfer items from one list to another by selecting the item to be transferred in one of the lists and choosing the appropriate transfer button 5 or 6. The transferred item is added to the end of the destination list and removed from the source list.
  • They can double-click on an item in List 1. This item is then transferred to the input box for editing and removed from List 1.

The buttons are enabled or disabled according to the following rules:

  • the Add button is enabled only if there is non-empty text in the input field
  • Button 5 for transferring from List 1 to List 2 is enabled only if an item is selected in List 1
  • Button 6 for transferring from List 2 to List 1 is enabled only if an item is selected in List 2
  • Buttons 7 and 8 for clearing lists 1 and 2 are enabled only if the list to be cleared contains items.

Under the above conditions, all buttons must be disabled when the application starts. This means the Enabled property of the buttons must be set to false. This can be done during design time, which will generate the corresponding code in the InitializeComponent method, or we can do it ourselves in the constructor as shown below:


    Public Sub New()
        ' Initial creation of the form
        InitializeComponent()
        ' Additional initializations
        ' Disable a number of buttons
        btnAdd.Enabled = False
        btn1TO2.Enabled = False
        btn2TO1.Enabled = False
        btnDelete1.Enabled = False
        btnDelete2.Enabled = False
    End Sub

The state of the Add button is controlled by the content of the text input field. The TextChanged event allows us to track changes to this content:


    ' change in text input field
    Private Sub txtInput_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles txtInput.TextChanged
        ' the content of txtSaisie has changed
        ' the Add button is enabled only if the text is not empty
        btnAdd.Enabled = txtInput.Text.Trim() <> ""
    End Sub

The state of the transfer buttons depends on whether or not an item has been selected in the list they control:


    ' Change in the selected item without ListBox1
    Private Sub listBox1_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles listBox1.SelectedIndexChanged

        ' an item has been selected
        ' Enable the button to transfer from 1 to 2
        btn1TO2.Enabled = True
    End Sub

    ' Change the selected item without listbox2
    Private Sub listBox2_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles listBox2.SelectedIndexChanged
        ' An item has been selected
        ' Enable the button to transfer from 2 to 1
        btn2TO1.Enabled = True
    End Sub

The code associated with clicking the Add button is as follows:


    ' Click on the Add button
    Private Sub btnAdd_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btnAdd.Click
        ' Add a new item to List 1
        listBox1.Items.Add(txtInput.Text.Trim())
        ' Clear the input
        txtSaisie.Text = ""
        ' List 1 is not empty
        btnClear1.Enabled = True
        ' Return focus to the input box
        txtInput.Focus()
    End Sub

Note the Focus method, which allows you to set the "focus" on a form control. The code associated with clicking the Clear buttons:


    ' Click on btnClear1
    Private Sub btnClear1_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btnDelete1.Click
        ' Clear list 1
        listBox1.Items.Clear()
        btnClear1.Enabled = False
    End Sub

    ' Click on btnClear2
    Private Sub btnClear2_Click(ByVal sender As Object, ByVal e As System.EventArgs)
        ' Clear list 2
        listBox2.Items.Clear()
        btnClear2.Enabled = False
    End Sub

The code to transfer selected items from one list to another:


    ' Click on btn1to2
    Private Sub btn1TO2_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btn1TO2.Click
        ' Transfer the selected item from List 1 to List 2
        transfer(listBox1, listBox2)
        ' Clear buttons
        btnClear2.Enabled = True
        btnClear1.Enabled = listBox1.Items.Count <> 0
        ' Transfer buttons
        btn1TO2.Enabled = False
        btn2TO1.Enabled = False
    End Sub

    ' Click on btn2to1 button
    Private Sub btn2TO1_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btn2TO1.Click
        ' Transfer the selected item from List 2 to List 1
        transfer(listBox2, listBox1)
        ' Clear buttons
        btnClear1.Enabled = True
        btnClear2.Enabled = listBox2.Items.Count <> 0
        ' Transfer buttons
        btn1TO2.Enabled = False
        btn2TO1.Enabled = False
    End Sub

    ' transfer
    Private Sub transfer(ByVal l1 As ListBox, ByVal l2 As ListBox)
        ' Transfer the selected item from ListBox1 to ListBox2
        ' Is an item selected?
        If l1.SelectedIndex = -1 Then Return
        ' Add to l2
        l2.Items.Add(l1.SelectedItem)
        ' Remove from l1
        l1.Items.RemoveAt(l1.SelectedIndex)
    End Sub

First, we create a method


    Private Sub transfer(ByVal l1 As ListBox, ByVal l2 As ListBox)

that transfers the selected item from list l1 to list l2. This allows us to use a single method instead of two to transfer an item from ListBox1 to ListBox2 or from ListBox2 to ListBox1. Before performing the transfer, we ensure that an item is indeed selected in list l1:


        ' Is an item selected?
        If l1.SelectedIndex = -1 Then Return

The SelectedIndex property is -1 if no item is currently selected. In the procedures


    Private Sub btnXTOY_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btnXTOY.Click

we transfer the contents of list X to list Y and update the state of certain controls to reflect the new state of the lists.

5.4.5. CheckBox checkboxes, ButtonRadio radio buttons

We propose to write the following application:

The window components are as follows:

No.
Type
name
role
1
RadioButton
radioButton1
radioButton2
radioButton3
3 radio buttons
2
CheckBox
checkBox1
checkBox2
checkbox3
3 checkboxes
3
ListBox
lstValues
a list

If we create the three radio buttons one after the other, they are part of the same group by default. Therefore, when one is selected, the others are not. The event we are interested in for these six controls is the CheckChanged event, which indicates that the state of the checkbox or radio button has changed. This state is represented in both cases by the Boolean property Check, which, when true, means that the control is checked. Here, we have used a single method to handle all six CheckChanged events; the method displays:


  'display
    Private Sub display(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles checkBox1.CheckedChanged, checkBox2.CheckedChanged, checkBox3.CheckedChanged, _
    radioButton1.CheckedChanged, radioButton2.CheckedChanged, radioButton3.CheckedChanged
        ' displays the state of the radio button or checkbox
        ' Is it a checkbox?
        If (TypeOf(sender) Is CheckBox) Then
            Dim chk As CheckBox = CType(sender, CheckBox)
            lstValues.Items.Insert(0, chk.Name & "=" & chk.Checked)
        End If
        ' Is it a radio button?
        If (TypeOf (sender) Is RadioButton) Then
            Dim rdb As RadioButton = CType(sender, RadioButton)
            lstValues.Items.Insert(0, rdb.Name & "=" & rdb.Checked)
        End If
    End Sub

The syntax TypeOf (sender) Is CheckBox allows us to check if the sender object is of type CheckBox. This then allows us to cast it to the exact type of sender. The method displays the name of the component that triggered the event and the value of its Checked property in the lstValeurs list. At runtime, we see that clicking a radio button triggers two CheckChanged events: one on the previously checked button, which becomes "unchecked," and the other on the new button, which becomes "checked."

5.4.6. ScrollBar controls

There are several types of scroll bars: the horizontal scroll bar (hScrollBar),
the vertical scroll bar (vScrollBar), and the numeric up/down control (NumericUpDown).

Let’s create the following application:

No.
type
name
role
1
hScrollBar
hScrollBar1
a horizontal scrollbar
2
hScrollBar
hScrollBar2
a horizontal slider that follows the changes in slider 1
3
TextBox
txtValue
displays the value of the horizontal slider
ReadOnly=true to prevent any input
4
NumericUpDown
incrementer
allows you to set the value of slider 2
  • A ScrollBar slider allows the user to select a value from a range of integer values represented by the slider's "band," along which a cursor moves. The slider's value is available in its Value property.
  • For a horizontal slider, the left end represents the minimum value of the range, the right end the maximum value, and the cursor the currently selected value. For a vertical slider, the minimum is represented by the top end, and the maximum by the bottom end. These values are represented by the Minimum and Maximum properties and default to 0 and 100.
  • Clicking on the ends of the slider changes the value by an increment (positive or negative) based on the clicked end, referred to as SmallChange, which defaults to 1.
  • Clicking on either side of the slider changes the value by one increment (positive or negative) depending on which end is clicked, a setting called LargeChange, which defaults to 10.
  • When you click on the top end of a vertical slider, its value decreases. This may surprise the average user, who normally expects to see the value "increase." You can resolve this issue by setting the SmallChange and LargeChange properties to negative values
  • These five properties (Value, Minimum, Maximum, SmallChange, LargeChange) are accessible for both reading and writing.
  • The slider’s main event is the one that signals a change in value: the Scroll event.

A NumericUpDown component is similar to a slider: it also has Minimum, Maximum, and Value properties, with default values of 0, 100, and 0, respectively. However, in this case, the Value property is displayed in an input box that is an integral part of the control. The user can modify this value themselves unless the control’s ReadOnly property has been set to true. The increment value is set by the Increment property, which defaults to 1. The main event of the NumericUpDown component is the one that signals a value change: the ValueChanged event. The relevant code for our application is as follows:

The form is formatted during its construction:


    ' constructor
    Public Sub New()
        ' initial form creation
        InitializeComponent()
        ' we give drive 2 the same characteristics as drive 1
        hScrollBar2.Minimum = hScrollBar1.Value
        hScrollBar2.Minimum = hScrollBar1.Minimum
        hScrollBar2.Maximum = hScrollBar1.Maximum
        hScrollBar2.LargeChange = hScrollBar1.LargeChange
        hScrollBar2.SmallChange = hScrollBar1.SmallChange
        ' same for the counter
        incrementer.Minimum = hScrollBar1.Value
        incrementer.Minimum = hScrollBar1.Minimum
        incrementer.Maximum = hScrollBar1.Maximum
        incrementer.Increment = hScrollBar1.SmallChange
        ' Set the TextBox to the value of slider 1
        txtValue.Text = "" & hScrollBar1.Value
    End Sub

The handler that tracks changes in the value of slider 1:


    ' hscrollbar1 slider handler
    Private Sub hScrollBar1_Scroll(ByVal sender As Object, ByVal e As System.Windows.Forms.ScrollEventArgs) _
    Handles hScrollBar1.Scroll
        ' change in the value of slider 1
        ' update its value on slider 2 and in the TxtValue textbox
        hScrollBar2.Value = hScrollBar1.Value
        txtValue.Text = "" & hScrollBar1.Value
    End Sub

The handler that tracks changes in the value of slider 2:


    ' hscrollbar2 slider handler
    Private Sub hScrollBar2_Scroll(ByVal sender As Object, ByVal e As System.Windows.Forms.ScrollEventArgs) _
    Handles hScrollBar2.Scroll
        ' We prevent any changes to slider 2
        ' by forcing it to retain the value of slider 1
        e.NewValue = hScrollBar1.Value
    End Sub

The handler that tracks changes in the scroll bar:


    ' Incrementer handler
    Private Sub Incrementer_ValueChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles Incrementer.ValueChanged
        ' Set the value of drive 2
        hScrollBar2.Value = CType(incrementer.Value, Integer)
    End Sub

5.5. Mouse Events

When drawing in a container, it is important to know the mouse position, for example, to display a point when clicked. Mouse movements trigger events in the container within which the mouse is moving.

 
The mouse has just entered the control's area
MouseLeave
The mouse has just left the control's area
MouseMove
The mouse is moving within the control's area
MouseDown
Left mouse button pressed
MouseUp
Left mouse button released
DragDrop
The user drops an object onto the control
DragEnter
The user enters the control's area while dragging an object
DragLeave
The user exits the control's area while dragging an object
DragOver
The user moves over the control's area while dragging an object

Here is a program to help you better understand when the various mouse events occur:

No.
type
name
role
1
Label
lblPosition
to display the mouse position in form 1, list 2, or button 3
2
ListBox
lstEvts
to display mouse events other than MouseMove
3
Button
btnClear
to clear the contents of 2

The event handlers are as follows. To track mouse movements across the three controls, we write a single handler:


    ' event form1_mousemove
    Private Sub Form1_MouseMove(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) _
    Handles MyBase.MouseMove, lstEvts.MouseMove, btnClear.MouseMove, lstEvts.MouseMove
        ' mouse move - display its coordinates (X,Y)
        lblPosition.Text = "(" & e.X & "," & e.Y & ")"
    End Sub

It is important to note that every time the mouse enters the area of a control, its coordinate system changes. Its origin (0,0) is the upper-left corner of the control it is currently on. Thus, during execution, when moving the mouse from the form to the button, the change in coordinates is clearly visible. To better see these changes in the mouse’s scope, you can use the Cursor property of the controls:

Image

This property allows you to set the shape of the mouse cursor when it enters the control’s area. Thus, in our example, we have set the cursor to Default for the form itself, Hand for List 2, and No for Button 3, as shown in the screenshots below.

Image

Image

Image

In the [InitializeComponent] method, the code generated by these choices is as follows:


        Me.lstEvts.Cursor = System.Windows.Forms.Cursors.Hand
        Me.btnEffacer.Cursor = System.Windows.Forms.Cursors.No

Additionally, to detect mouse entries and exits on List 2, we handle the MouseEnter and MouseLeave events for that list:


    ' evt lstEvts_MouseEnter
    Private Sub lstEvts_MouseEnter(ByVal sender As System.Object, ByVal e As System.EventArgs) _
    Handles lstEvts.MouseEnter
        display("MouseEnter on list")
    End Sub

    ' lstEvts_MouseLeave event
    Private Sub lstEvts_MouseLeave(ByVal sender As System.Object, ByVal e As System.EventArgs) _
    Handles lstEvts.MouseLeave
        display("MouseLeave on list")
    End Sub
    
' display
    Private Sub display(ByVal message As String)
        ' displays the message at the top of the evts list
        lstEvts.Items.Insert(0, message)
    End Sub

Image

To handle clicks on the form, we handle the MouseDown and MouseUp events:


    ' Form1_MouseDown event
    Private Sub Form1_MouseDown(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) _
    Handles MyBase.MouseDown
        display("MouseDown on form")
    End Sub

    ' Form1_MouseUp event
    Private Sub Form1_MouseUp(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) _
    Handles MyBase.MouseUp
        display("MouseUp on form")
    End Sub

Image

Finally, the code for the Click event handler on the Delete button:


    ' btnClear_Click
    Private Sub btnDelete_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
    Handles btnDelete.Click
        ' clears the list of events
        lstEvts.Items.Clear()
    End Sub

5.6. Create a window with a menu

Now let's see how to create a window with a menu. We will create the following window:

Control 1 is a read-only TextBox (ReadOnly=true) named txtStatut. The menu tree is as follows:

Menu options are controls just like other visual components and have properties and events. For example, the property table for menu option A1:

Image

Two properties are used in our example:

 
the name of the menu control
Text
the label of the menu option

The properties of the various menu options in our example are as follows:

Name
Text
mnuA
options A
mnuA1
A1
mnuA2
A2
mnuA3
A3
mnuB
options B
mnuB1
B1
mnuSep1
- (separator)
mnuB2
B2
mnuB3
B3
mnuB31
B31
mnuB32
B32

To create a menu, select the "MainMenu" component from the "ToolBox" bar:

Image

You will then have an empty menu placed on the form with empty boxes labeled "Type Here." Simply enter the various menu options there:

Image

To insert a separator between two options, as shown above between options B1 and B2, position the cursor where you want the separator to appear in the menu, right-click, and select the Insert Separator option:

Image

If you run the application using Ctrl+F5, you’ll see a form with a menu that doesn’t do anything yet. Menu options are treated as components: they have properties and events. In the [code window], select the mnuA1 component, then select the associated events:

Image

If you trigger the Click event above, VS.NET automatically generates the following procedure:


Private Sub mnuA1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles mnuA.Click
....
    End Sub

We could proceed this way for all menu options. Here, the same procedure can be used for all options. So we rename the previous procedure *to display* and declare the events it handles:


    Private Sub display(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles mnuA1.Click, mnuA2.Click, mnuB1.Click, mnuB2.Click, mnuB31.Click, mnuB32.Click
        ' Displays the name of the selected submenu in the TextBox
        txtStatut.Text = (CType(sender, MenuItem)).Text
    End Sub

In this method, we simply display the Text property of the menu option that triggered the event. The event source sender is of type Object. The menu options are of type MenuItem, so we must cast from Object to MenuItem here. Run the application and select option A1 to see the following message:

Image

The relevant code for this application, aside from the Display method, is the code for building the menu in the form’s constructor (InitializeComponent):


  Private Sub InitializeComponent()
    Me.mainMenu1 = New System.Windows.Forms.MainMenu
    Me.mnuA = New System.Windows.Forms.MenuItem
    Me.mnuA1 = New System.Windows.Forms.MenuItem
    Me.mnuA2 = New System.Windows.Forms.MenuItem
    Me.mnuA3 = New System.Windows.Forms.MenuItem
    Me.mnuB = New System.Windows.Forms.MenuItem
    Me.mnuB1 = New System.Windows.Forms.MenuItem
    Me.mnuB2 = New System.Windows.Forms.MenuItem
    Me.mnuB3 = New System.Windows.Forms.MenuItem
    Me.mnuB31 = New System.Windows.Forms.MenuItem
    Me.mnuB32 = New System.Windows.Forms.MenuItem
    Me.txtStatus = New System.Windows.Forms.TextBox
    Me.mnuSep1 = New System.Windows.Forms.MenuItem
    Me.SuspendLayout()
    ' 
    ' mainMenu1
    ' 
    Me.mainMenu1.MenuItems.AddRange(New System.Windows.Forms.MenuItem() {Me.mnuA, Me.mnuB})

    ' 
    ' mnuA
    ' 
    Me.mnuA.Index = 0
    Me.mnuA.MenuItems.AddRange(New System.Windows.Forms.MenuItem() {Me.mnuA1, Me.mnuA2, Me.mnuA3})
    Me.mnuA.Text = "Options A"
    ' 
    ' mnuA1
    ' 
    Me.mnuA1.Index = 0
    Me.mnuA1.Text = "A1"
        ' 
    ' mnuA2
    ' 
    Me.mnuA2.Index = 1
    Me.mnuA2.Text = "A2"
        ' 
    ' mnuA3
    ' 
    Me.mnuA3.Index = 2
    Me.mnuA3.Text = "A3"
        ' 
    ' mnuB
    ' 
    Me.mnuB.Index = 1
    Me.mnuB.MenuItems.AddRange(New System.Windows.Forms.MenuItem() {Me.mnuB1, Me.mnuSep1, Me.mnuB2, Me.mnuB3})

    Me.mnuB.Text = "Options B"
    ' 
    ' mnuB1
    ' 
    Me.mnuB1.Index = 0
    Me.mnuB1.Text = "B1"
        ' 
    ' mnuB2
    ' 
    Me.mnuB2.Index = 2
    Me.mnuB2.Text = "B2"

    ' 
    ' mnuB3
    ' 
    Me.mnuB3.Index = 3
    Me.mnuB3.MenuItems.AddRange(New System.Windows.Forms.MenuItem() {Me.mnuB31, Me.mnuB32})
    Me.mnuB3.Text = "B3"
        ' 
    ' mnuB31
    ' 
    Me.mnuB31.Index = 0
    Me.mnuB31.Text = "B31"
        ' 
    ' mnuB32
    ' 
    Me.mnuB32.Index = 1
    Me.mnuB32.Text = "B32"
        ' 
    ' txtStatus
    ' 
    Me.txtStatus.Location = New System.Drawing.Point(8, 8)
    Me.txtStatus.Name = "txtStatus"
    Me.txtStatus.ReadOnly = True
    Me.txtStatus.Size = New System.Drawing.Size(112, 20)
    Me.txtStatus.TabIndex = 0
    Me.txtStatus.Text = ""
    ' 
    ' mnuSep1
    ' 
    Me.mnuSep1.Index = 1
    Me.mnuSep1.Text = "-"
    ' 
    ' Form1
    ' 
    Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
    Me.ClientSize = New System.Drawing.Size(136, 42)
    Me.Controls.Add(txtStatus)
    Me.Menu = Me.mainMenu1
    Me.Name = "Form1"
    Me.Text = "Menus"
    Me.ResumeLayout(False)
  End Sub

Note the statement that associates the menu with the form:


    Me.Menu = Me.mainMenu1

5.7. Non-visual components

We will now look at a number of non-visual components: these are used during design but are not visible during runtime.

5.7.1. OpenFileDialog and SaveFileDialog dialog boxes

We are going to build the following application:

The controls are as follows:

No.
Type
name
role
1
Multi-line TextBox
txtText
text typed by the user or loaded from a file
2
Button
btnSave
saves the text from 1 to a text file
3
Button
btnLoad
allows you to load the contents of a text file into 1
4
Button
btnClear
clears the contents of 1

Two non-visual controls are used:

Image

When they are dragged from the "ToolBox" and dropped onto the form, they are placed in a separate area at the bottom of the form. The "Dialog" components are dragged from the "ToolBox":

Image

The code for the Clear button is simple:


    Private Sub btnDelete_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btnClear.Click
        ' clear the text box
        txtText.Text = ""
    End Sub

The SaveFileDialog class is defined as follows:

Image

It inherits from several class levels. Of its many properties and methods, we will focus on the following:

string Filter
the file types offered in the file type drop-down list of the dialog box
int FilterIndex
the index of the file type displayed by default in the list above. Starts at 0.
String
InitialDirectory
the folder initially displayed for saving the file
string FileName
The name of the save file specified by the user
DialogResult.ShowDialog()
A method that displays the save dialog box. Returns a DialogResult.

The ShowDialog method displays a dialog box similar to the following:

1
drop-down list generated from the Filter property. The default file type is determined by FilterIndex
2
current directory, set by InitialDirectory if this property has been specified
3
the name of the file selected or typed directly by the user. Will be available in the FileName property
4
Save/Cancel buttons. If the Save button is used, the ShowDialog function returns the result DialogResult.OK

The save procedure can be written as follows:


    Private Sub btnSave_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btnSave.Click
        ' Save the text box to a text file
        ' configure the savefileDialog1 dialog box
        saveFileDialog1.InitialDirectory = Application.ExecutablePath
        saveFileDialog1.Filter = "Text files (*.txt)|*.txt|All files (*.*)|*.*"
        saveFileDialog1.FilterIndex = 0
        ' display the dialog box and retrieve its result
        If saveFileDialog1.ShowDialog() = DialogResult.OK Then
            ' retrieve the file name
            Dim fileName As String = saveFileDialog1.FileName
            Dim file As StreamWriter = Nothing
            Try
                ' open the file for writing
                file = New StreamWriter(fileName)
                ' write the text to it
                file.Write(txtText.Text)
            Catch ex As Exception
                ' problem
                MessageBox.Show("Problem writing the file (" + ex.Message + ")", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
                Return
            Finally
                ' close the file
                Try
                    file.Close()
                Catch
                End Try
            End Try
        End If
    End Sub
  • We set the initial directory to the directory containing the application's executable file:
        saveFileDialog1.InitialDirectory = Application.ExecutablePath
  • We set the file types to display
        saveFileDialog1.Filter = "Text files (*.txt)|*.txt|All files (*.*)|*.*"

Note the filter syntax: filter1|filter2|..|filteren, where filteri = Text|file type. Here, the user can choose between *.txt and *.* files.

  • We set the file type to display at the beginning
        saveFileDialog1.FilterIndex = 0

Here, files of type *.txt will be displayed to the user first.

  • The dialog box is displayed and its result is retrieved

    If saveFileDialog1.ShowDialog() = DialogResult.OK Then
  • While the dialog box is displayed, the user no longer has access to the main form (a so-called modal dialog box). The user sets the name of the file to be saved and exits the dialog box either by clicking the Save button, the Cancel button, or by closing the dialog box. The result of the ShowDialog method is DialogResult.OK only if the user used the Save button to exit the dialog box.
  • Once this is done, the name of the file to be created is now in the FileName property of the saveFileDialog1 object. We then return to the standard process of creating a text file. We write the contents of the TextBox: txtTexte.Text, while handling any exceptions that may occur.

The OpenFileDialog class is very similar to the SaveFileDialog class and derives from the same class lineage. From these properties and methods, we will focus on the following:

string Filter
the file types offered in the file type drop-down list of the dialog box
int FilterIndex
the index of the file type displayed by default in the list above. Starts at 0.
string InitialDirectory
the directory initially displayed for searching for the file to open
string FileName
The name of the file to open, as specified by the user
DialogResult.ShowDialog()
A method that displays the save dialog box. Returns a DialogResult.

The ShowDialog method displays a dialog box similar to the following:

1
drop-down list built from the Filter property. The default file type is determined by FilterIndex
2
current folder, set by InitialDirectory if this property has been specified
3
Name of the file selected or typed directly by the user. Will be available in the FileName property
4
Open/Cancel buttons. If the Open button is used, the ShowDialog function returns the result DialogResult.OK

The open procedure can be written as follows:


Private Sub btnCharger_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btnCharger.Click
        ' loads a text file into the input box
        ' configure the openfileDialog1 dialog box
        openFileDialog1.InitialDirectory = Application.ExecutablePath
        openFileDialog1.Filter = "Text files (*.txt)|*.txt|All files (*.*)|*.*"
        openFileDialog1.FilterIndex = 0
        ' display the dialog box and retrieve its result
        If openFileDialog1.ShowDialog() = DialogResult.OK Then
            ' retrieve the file name
            Dim fileName As String = openFileDialog1.FileName
            Dim file As StreamReader = Nothing
            Try
                ' open the file for reading
                file = New StreamReader(fileName)
                ' read the entire file and put it in the TextBox
                txtText.Text = file.ReadToEnd()
            Catch ex As Exception
                ' problem
                MessageBox.Show("Problem reading the file (" + ex.Message + ")", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
                Return
            Finally
                ' Close the file
                Try
                    file.Close()
                Catch
                End Try
            End Try
        End If
    End Sub
  • We set the initial directory to the directory containing the application's executable:
            saveFileDialog1.InitialDirectory = Application.ExecutablePath
  • We set the file types to display
            saveFileDialog1.Filter = "Text files (*.txt)|*.txt|All files (*.*)|*.*"
  • Set the file type to display first
            saveFileDialog1.FilterIndex = 0

Here, *.txt files will be displayed to the user first.

  • The dialog box is displayed and its result is retrieved

    If openFileDialog1.ShowDialog() = DialogResult.OK Then

While the dialog box is displayed, the user no longer has access to the main form (a so-called modal dialog box). The user specifies the name of the file to open and exits the dialog box either by clicking the Open button, the Cancel button, or by closing the dialog box. The result of the ShowDialog method is DialogResult.OK only if the user used the Open button to exit the dialog box.

  • Once this is done, the name of the file to be created is now in the FileName property of the openFileDialog1 object. We then return to the standard process of reading a text file. Note the method that allows you to read the entire file:
                    txtText.Text = file.ReadToEnd
  • The file's contents are placed in the txtTexte TextBox. We handle any exceptions that may occur.

5.7.2. FontColor and ColorDialog dialog boxes

We continue the previous example by introducing two new buttons:

No.
type
name
role
6
Button
btnColor
to set the text color of the TextBox
7
Button
btnFont
to set the font of the TextBox

We place a ColorDialog control and a FontDialog control on the form:

Image

The FontDialog and ColorDialog classes have a ShowDialog method similar to the ShowDialog method of the OpenFileDialog and SaveFileDialog classes. The ShowDialog method of the ColorDialog class allows you to choose a color:

Image

If the user closes the dialog box using the OK button, the result of the ShowDialog method is DialogResult.OK, and the selected color is stored in the Color property of the ColorDialog object used. The ShowDialog method of the FontDialog class allows you to select a font:

Image

If the user closes the dialog box by clicking the OK button, the result of the ShowDialog method is DialogResult.OK, and the selected font is stored in the Font property of the FontDialog object used. We have the elements to handle clicks on the Color and Font buttons:


    Private Sub btnColor_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btnColor.Click
        ' Select a text color
        If colorDialog1.ShowDialog() = DialogResult.OK Then
            ' change the TextBox's ForeColor property
            txtText.ForeColor = colorDialog1.Color
        End If
    End Sub


    Private Sub btnFont_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btnFont.Click
        ' Select a font
        If fontDialog1.ShowDialog() = DialogResult.OK Then
            ' Change the font property of the TextBox
            txtText.Font = fontDialog1.Font
        End If
    End Sub

5.7.3. Timer

Here, we propose to write the following application:

No.
Type
Name
Role
1
TextBox, ReadOnly=true
txtChrono
displays a timer
2
Button
btnStopStart
Stop/Start button for the stopwatch
3
Timer
timer1
component that triggers an event every second

The timer is running:

Image

The timer stopped:

Image

To update the contents of the txtChrono TextBox every second, we need a component that generates an event every second, which we can intercept to update the stopwatch display. This component is the Timer:

Image

Once this component is added to the form (in the non-visual components section), a Timer object is created in the form’s constructor. The System.Windows.Forms.Timer class is defined as follows:

Image

Of its properties, we will only consider the following:

Interval
number of milliseconds after which a Tick event is triggered.
Tick
the event triggered at the end of Interval milliseconds
Enabled
sets the timer to active (true) or inactive (false)

In our example, the timer is called timer1 and timer1.Interval is set to 1000 ms (1s). The Tick event will therefore occur every second. A click on the Start/Stop button is handled by the following procedure:


    Private Sub btnStopStart_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles btnArretMarche.Click
        ' Stop or Start?
        If btnArretMarche.Text = "Start" Then
            ' record the start time
            start = DateTime.Now
            ' it is displayed
            txtChrono.Text = "00:00:00"
            ' start the timer
            timer1.Enabled = True
            ' change the button label
            btnArretMarche.Text = "Stop"
            ' end
            Return
        End If        '
        If btnStopStart.Text = "Stop" Then
            ' stop the timer
            timer1.Enabled = False
            ' change the button label
            btnArretMarche.Text = "Start"
            ' end
            Return
        End If
    End Sub

    Private Sub timer1_Tick(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles timer1.Tick
        ' One second has elapsed
        Dim now As DateTime = DateTime.Now
        Dim duration As TimeSpan = DateTime.op_Subtraction(now, start)
        txtChrono.Text = "" + duration.Hours.ToString("d2") + ":" + duration.Minutes.ToString("d2") + ":" + duration.Seconds.ToString("d2")
    End Sub

The label of the Start/Stop button is either "Stop" or "Start". We therefore have to check this label to determine what to do.

  • If the label is "Start", we store the start time in a global variable of the form object, the timer is started (Enabled=true), and the button label changes to "Stop".
  • If the label is "Stop", the timer is stopped (Enabled=false) and the button label is changed to "Start".

Public Class Timer1
  Inherits System.Windows.Forms.Form
  Private WithEvents timer1 As System.Windows.Forms.Timer
  Private WithEvents btnStopStart As System.Windows.Forms.Button
  Private components As System.ComponentModel.IContainer
  Private WithEvents txtChrono As System.Windows.Forms.TextBox
  Private WithEvents label1 As System.Windows.Forms.Label

  ' instance variables
  Private start As DateTime

The start attribute above is known in all methods of the class. We still need to handle the Tick event on the timer1 object, an event that occurs every second:


    Private Sub timer1_Tick(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles timer1.Tick
        ' one second has elapsed
        Dim now As DateTime = DateTime.Now
        Dim duration As TimeSpan = DateTime.op_Subtraction(now, start)
        txtChrono.Text = "" + duration.Hours.ToString("d2") + ":" + duration.Minutes.ToString("d2") + ":" + duration.Seconds.ToString("d2")
    End Sub

We calculate the time elapsed since the stopwatch was started. We obtain a TimeSpan object representing a duration. This must be displayed in the timer in the format hh:mm:ss. To do this, we use the Hours, Minutes, and Seconds properties of the TimeSpan object, which represent the hours, minutes, and seconds of the duration, respectively. We display them using the ToString("d2") format to ensure a two-digit display.

5.8. The tax calculation Example

We return to the IMPOTS application, which we have already covered twice. We now add a graphical user interface to it:

The controls are as follows

No.
type
name
role
1
RadioButton
rdYes
checked if married
2
RadioButton
rdNo
Checked if not married
3
NumericUpDown
incChildren
number of the taxpayer's children
Minimum=0, Maximum=20, Increment=1
4
TextBox
txtSalary
taxpayer's annual salary in F
5
TextBox
txtTaxes
amount of tax due
ReadOnly=true
6
Button
btnCalculate
starts the tax calculation
7
Button
btnClear
resets the form to its initial state upon loading
8
Button
btnExit
to exit the application

Operating Rules

  • The Calculate button remains disabled as long as the salary field is empty
  • If, when the calculation is run, the salary turns out to be incorrect, an error is reported:

Image

The program is shown below. It uses the Impot class created in the chapter on classes. Some of the code automatically generated by VS.NET has not been reproduced here.


using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
' options
Option Explicit On 
Option Strict On

' namespaces
Imports System
Imports System.Drawing
Imports System.Collections
Imports System.ComponentModel
Imports System.Windows.Forms
Imports System.Data

' form class
Public Class frmTaxes
  Inherits System.Windows.Forms.Form
  Private WithEvents label1 As System.Windows.Forms.Label
  Private WithEvents rdYes As System.Windows.Forms.RadioButton
  Private WithEvents rdNo As System.Windows.Forms.RadioButton
  Private WithEvents label2 As System.Windows.Forms.Label
  Private WithEvents txtSalary As System.Windows.Forms.TextBox
  Private WithEvents label3 As System.Windows.Forms.Label
  Private WithEvents label4 As System.Windows.Forms.Label
  Private WithEvents groupBox1 As System.Windows.Forms.GroupBox
  Private WithEvents btnCalculate As System.Windows.Forms.Button
  Private WithEvents btnClear As System.Windows.Forms.Button
  Private WithEvents btnExit As System.Windows.Forms.Button
  Private WithEvents txtTaxes As System.Windows.Forms.TextBox
  Private components As System.ComponentModel.Container = Nothing
  Private WithEvents incChildren As System.Windows.Forms.NumericUpDown

  ' data arrays required for tax calculation
  Private limits() As Decimal = {12620D, 13190D, 15640D, 24740D, 31810D, 39970D, 48360D, 55790D, 92970D, 127860D, 151250D, 172040D, 195000D, 0D}
  Private coeffR() As Decimal = {0D, 0.05D, 0.1D, 0.15D, 0.2D, 0.25D, 0.3D, 0.35D, 0.4D, 0.45D, 0.55D, 0.5D, 0.6D, 0.65D}
  Private coeffN() As Decimal = {0D, 631D, 1290.5D, 2072.5D, 3309.5D, 4900D, 6898.5D, 9316.5D, 12106D, 16754.5D, 23147.5D, 30710D, 39312D, 49062D}
  ' tax object
  Private objTax As Tax = Nothing


  Public Sub New()
    InitializeComponent()
    ' initialize the form
    btnClear_Click(Nothing, Nothing)
    btnCalculate.Enabled = False
    ' create a tax object
    Try
      objTax = New Tax(limits, coeffR, coeffN)
    Catch ex As Exception
      MessageBox.Show("Unable to create the tax object (" + ex.Message + ")", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
      ' Disable the salary input field
      txtSalary.Enabled = False
    End Try 'try-catch
  End Sub 

  Protected Overloads Sub Dispose(ByVal disposing As Boolean)
....
  End Sub

  Private Sub InitializeComponent()
    Me.btnQuit = New System.Windows.Forms.Button
    Me.groupBox1 = New System.Windows.Forms.GroupBox
    Me.btnClear = New System.Windows.Forms.Button
    Me.btnCalculate = New System.Windows.Forms.Button
    Me.txtSalary = New System.Windows.Forms.TextBox
    Me.label1 = New System.Windows.Forms.Label
    Me.label2 = New System.Windows.Forms.Label
    Me.label3 = New System.Windows.Forms.Label
    Me.rdNon = New System.Windows.Forms.RadioButton
    Me.txtTaxes = New System.Windows.Forms.TextBox
    Me.label4 = New System.Windows.Forms.Label
    Me.rdYes = New System.Windows.Forms.RadioButton
    Me.incEnfants = New System.Windows.Forms.NumericUpDown
    Me.groupBox1.SuspendLayout()
    CType(Me.incEnfants, System.ComponentModel.ISupportInitialize).BeginInit()
    Me.SuspendLayout()
....
  End Sub 'InitializeComponent

  Public Shared Sub Main()
    Application.Run(New frmImpots)
  End Sub 'Main


  Private Sub btnClear_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
  Handles btnClear.Click
    ' Clear the form
    incEnfants.Value = 0
    txtSalary.Text = ""
    txtTaxes.Text = ""
    rdNon.Checked = True
  End Sub 'btnClear_Click


  Private Sub txtSalary_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
  Handles txtSalary.TextChanged
    ' Calculate button state
    btnCalculate.Enabled = txtSalary.Text.Trim() <> ""
  End Sub 'txtSalary_TextChanged


  Private Sub btnQuitter_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
  Handles btnExit.Click
    ' End Application
    Application.Exit()
  End Sub 'btnQuitter_Click


  Private Sub btnCalculate_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
  Handles btnCalculate.Click
    ' Is the salary correct?
    Dim intSalary As Integer = 0
    Try
      ' Retrieve salary
      intSalary = Integer.Parse(txtSalary.Text)
      ' it must be >= 0
      If intSalary < 0 Then
        Throw New Exception("")
      End If
    Catch ex As Exception
      ' error message
      MessageBox.Show(Me, "Incorrect salary", "Input error", MessageBoxButtons.OK, MessageBoxIcon.Error)
      ' focus on the incorrect field
      txtSalary.Focus()
      ' Select text in the input field
      txtSalary.SelectAll()
      ' return to the visual interface
      Return
    End Try 'try-catch
    ' the salary is correct - calculate the tax
    txtTaxes.Text = "" & CLng(objTax.calculate(rdYes.Checked, CInt(childrenCount.Value), intSalary))
  End Sub 'btnCalculate_Click
End Class

Here we use the impots.dll assembly, which is the result of compiling the impots class from Chapter 2. Recall that this assembly can be generated in console mode using the command

dos>vbc /t:library impots.vb

This command generates the impots.dll file, known as an assembly. This assembly can then be used in various projects. Here, in our project under VS.NET, we use the project properties window:

Image

To add a reference (an assembly), we right-click on the References group above, select the [Add Reference] option, and specify the [impots.dll] assembly that we have placed in the project folder:

dos>dir
03/01/2004  14:39                9,250 gui_impots.vb
03/01/2004  14:37                4,096 impots.dll
03/01/2004  14:41               12,288 gui_impots.exe

Once the assembly [impots.dll] is included in the project, the [impots] class becomes known to the project. Previously, it was not. Another method is to include the source file impots.vb in the project. To do this, in the project properties window, right-click on the project, select the [Add/Add Existing Item] option, and specify the impots.vb file.