Developing Desktop Applications

Windows Forms is a set of classes that encapsulates the creation of the graphical user interface (GUI) portion of a typical desktop application. Previously, each programming language had its own way of creating windows, text boxes, buttons, etc. 


This functionality has all been moved into the .NET Framework class library—into the types located in the System.Windows.Forms namespace. Closely related is the System.Drawing namespace, which contains several types used in the creation of GUI applications. The capabilities provided by the types in the System.Drawing namespace are commonly referred to as GDI+. In here, we'll examine the form (or window) as the central component in a classic desktop application. We'll look at how forms are programmatically created and how they're hooked to events. We'll also examine how multiple forms in a single application relate to one another and how you handle forms in an application that has one or more child forms. Finally, we'll discuss two topics, printing and 2-D graphics, that are relevant to desktop application development.


Creating a Form

The easiest way to design a form is to use the Windows Forms Designer in Visual Studio .NET. The developer can use visual tools to lay out the form, with the designer translating the layout into Visual Basic .NET source code. If you don't have Visual Studio .NET, you can write the Visual Basic .NET code directly and not use the designer at all. This section will demonstrate both methods. Programmatically, a form is defined by deriving a class from the Form class (defined in System.Windows.Forms). The Form class contains the know-how for displaying an empty form, including its title bar and other amenities that we expect from a Windows form. Adding members to the new class and overriding members inherited from the Form class add visual elements and behavior to the new form.

Creating a Form Using Visual Studio .NET
To create a GUI application in Visual Studio .NET:
  1. Select File New Project. The New Project dialog box appears, as shown in Figure 3
  2. Select Visual Basic Projects in the Project Types pane on the left side of the dialog box.
  3. Select Windows Application in the Templates pane on the right side of the dialog box.
  4. Enter a name in the Name text box.
  5. Figure 3
  6. Click OK. Visual Studio .NET creates a project with a form in it and displays the form in a designer, as shown in Figure 4
Figure 4
To see the code created by the form Windows Forms Designer, right-click on the form, then select View Code. Doing this for the blank form shown in Figure 4 reveals the code shown here:
CODES:

This shows the definition of a class named Form1 that inherits from the Form class. The Windows Forms Designer also creates a lot of boilerplate code that should not be modified by the developer. By default, it hides this code from view. To see the code, click on the "+" symbol that appears to the left of the line that says "Windows Form Designer generated code." Doing so reveals the code shown in Example 4-1

Example 4-1. The Windows Forms Designer-generated code for a blank form
CODES:

The Windows Forms Designer autogenerates the code for four class members:

New method (the class constructor)

The constructor calls the base class's constructor and then invokes the InitializeComponent method. Developer-supplied initialization code should follow the call to InitializeComponent. After the constructor is generated, the designer doesn't touch it again.

Dispose method
The Dispose method is where the object gets rid of any expensive resources. In this case, it calls the base class's Dispose method to give it a chance to release any expensive resources that it may hold, then it calls the components field's Dispose method. (For more on the components field, see the next item.) This in turn calls the Dispose methods on each individual component in the collection. If the derived class uses any expensive resources, the developer should add code here to release them. When a form is no longer needed, all code that uses the form should call the form's Dispose method. After the Dispose method is generated, the designer doesn't touch it again.

Components field
The components field is an object of type IContainer (defined in the System.ComponentModel namespace). The designer-generated code uses the components field to manage finalization of components that may be added to a form (for example, the Timer component).

InitializeComponent method
The code in this method should not be modified or added to by the developer in any way. The Windows Forms Designer automatically updates it as needed. When controls are added to the form using the designer, code is added to this method to instantiate the controls at runtime and set their initial properties. Note also in Example 4-1 that properties of the form itself (such as Text and Name) are initialized in this method. One thing missing from this class definition is a Main method. .NET applications must expose a public, shared Main method. This method is called by the CLR when an application is started. So why doesn't the designer-generated form include a Main method? It's because the Visual Basic .NET compiler in Visual Studio .NET automatically creates one as it compiles the code. In other words, the compiled code has a Main method in it even though the source code does not. The Main method in the compiled code is a member of the Form1 class and is equivalent to this:
CODES:
Note that the Visual Basic .NET command-line compiler doesn't automatically generate the Main method. This method must appear in the source code if the command-line compiler is to be used.

The next steps in designing the form are to name the code file something meaningful and to set some properties on the form, such as the title-bar text. To change the name of the form's code file, right-click on the filename in the Solution Explorer window and select Rename. If you're following along with this example, enter HelloWindows.vb as the name of the file. Changing the name of the file doesn't change the name of the class. To change the name of the class, right-click the form in the designer and choose Properties. In the Properties window, change the value of the Name property. For this example, change the name to "HelloWindows". To change the form's caption, set the form's Text property to a new value. Set the Text property in this example to "Programming Visual Basic .NET". Next, controls can be added to the form from the Visual Studio .NET toolbox. To display the toolbox, select View Toolbox from the Visual Studio .NET main menu. For this example, double-click on the Label control in the toolbox to add a Label control on the form. Use the Properties window to change the label's Text property to "Hello, Windows!" and its Font property to Arial 24pt. Next, double-click on the Button control in the toolbox to add a Button control to the form. Use the Properties window to change the button's Name property to "OkButton" and its Text property to "OK".

Finally, position the controls as desired, size the Label control and the form to be appealing, and set
the form's FormBorderStyle property to "FixedToolWindow". The resulting form should look something
like the one shown in Figure 5

Figure 5
Press the F5 key to build and run the program. The result should look something like Figure 5.
Figure 5. Hello, Windows!, as created by the Windows Forms Designer

Figure 5.
The code generated by the designer is shown in Example 4-2.

Example 4-2. Hello, Windows! code, as generated by the Windows Forms Designer
CODES:
Note that the designer made the following modifications to the code:
  • Two Friend fields were added to the class, one for each of the controls that were added to the form:
  • Friend WithEvents Label1 As System.Windows.Forms.Label
  •  Friend WithEvents OkButton As System.Windows.Forms.Button
  • The Friend keyword makes the members visible to other code within the project, but it hides them from code running in other assemblies.
  • The WithEvents keyword allows the HelloWindows class to handle events generated by the controls. In the code shown, no event handlers have been added yet, but you'll see how to do that later in this section.
  • Note that the field names match the control names as shown in the Properties window.
  • Code was added to the InitializeComponent method to instantiate the two controls and assign their references to the member fields:
  • Me.Label1 = New System.Windows.Forms.Label( )
  • Me.OkButton = New System.Windows.Forms.Button( )
  • Code was added to the InitializeComponent method to set various properties of the label, button, and form. Some of these assignments directly correspond to the settings made in the Properties window, while others are the implicit result of other actions taken in the designer (such as sizing the form).

Adding event handlers
The Hello, Windows! application built thus far has an OK button, but the application doesn't yet respond to button clicks. To add a Click event handler for the OK button, double-click on the button in the Windows Forms Designer. The designer responds by switching to the form's code view and inserting a subroutine that handles the Click event (i.e., it will be called when the user of the running application clicks the OK button). The subroutine the designer creates looks like this
CODES:
The body of the subroutine can then be added. This would be a likely implementation for this event
handler:
CODES:
Figure 6. Adding an event handler using the code view's drop-down

Figure 6
Event handlers can be typed directly into the form's code if you know the correct signature for the handler. Event-handler signatures are documented in the Microsoft Developer Network ( MSDN) Library.

Creating a Form in Code
Although form designers are convenient, it is certainly possible to code a form directly. To do so, follow
these steps:
1. Define a class that is derived from the Form class (defined in the System.Windows.Forms namespace). If the form is to be the startup form for an application, include a public, shared Main method. For example:

2. Imports System.Windows.Forms
3.
4. Public Class HelloWindows
5. Inherits Form
6.
7. ' Include this method only if this is the application's startup
form.
8. ' Alternatively, place this method in a separate module in the
9. ' application. If it is placed in a separate module, remove the
10. ' Shared keyword.
11. <System.STAThreadAttribute( )> Public Shared Sub Main( )
12. System.Threading.Thread.CurrentThread.ApartmentState = _
13. System.Threading.ApartmentState.STA
14. Application.Run(New HelloWindows( ))
15. End Sub ' Main
16.
Programming Visual Basic .NET
141
End Class
17. Declare a data member for each control that is to appear on the form. If you want to handle events from the control, use the WithEvents keyword in the declaration. For example:
18. Imports System.Windows.Forms
19.
20. Public Class HelloWindows
21. Inherits Form
22.
23. Private lblHelloWindows As Label
24. Private WithEvents btnOK As Button
25.
26. <System.STAThreadAttribute( )> Public Shared Sub Main( )
27. System.Threading.Thread.CurrentThread.ApartmentState = _
28. System.Threading.ApartmentState.STA
29. Application.Run(New HelloWindows( ))
30. End Sub ' Main
31. End Class
The visibility (Private, Friend, Protected, or Public) of these data members is a design issue that depends on the project and on the developer's preferences. My own preference is to make all data members private. If code external to the class needs to modify the data held by these members, specific accessor methods can be added for the purpose. This prevents internal design changes from affecting external users of the class.

32. Declare a constructor. Perform the following operations in the constructor:
           a. Instantiate each control.
           b. Set properties for each control and for the form.
           c. Add all controls to the form's Controls collection.

For example:
CODES:
An Imports statement was added to give access to types in the System.Drawing namespace, such as Point and Size.

Adding event handlers
Define event handlers directly in code for any events that you wish to handle. For example:
CODES:
The complete code for a standalone Windows Forms application is shown in Example 4-3. Compile it
from the command line with this command:
CODES:
(Note that the command should be typed on a single line.)

Example 4-3. Hello, Windows! code generated outside of Visual Studio
CODES:
Handling Form Events
The base Form class may at times raise events. These events can be handled by the derived Form class. One way to do this is to define a handler subroutine that uses the MyBase keyword in the Handles clause, like this:
CODES:
However, a better technique is to override the protected methods, which are provided by the Form class for this purpose. For example, the following method could be placed in the derived class's definition, providing a way to respond to the form's imminent closing:
CODES:
Note that the implementation of the OnClosing method includes a call to the base class's implementation. This is important. If this is not done, the Closing event won't be raised, which will affect the behavior of any other code that has registered for the event.

Following is the list of events the Form class defines, including a brief description of each event and the syntax for overriding the protected method that corresponds to each event. Note also that the Form class indirectly derives from the Control class and that the Control class also exposes events and overridable methods that aren't shown here.

Activated
Fired when the form is activated. Its syntax is:
CODES:
Closed
Fired when the form has been closed. Its syntax is:
CODES:
Closing
Fired when the form is about to close. Its syntax is:
CODES:
The CancelEventArgs.Cancel property can be set to True to prevent the form from closing; its
default value is False.

Deactivate
Fired when the form is deactivated. Its syntax is:
CODES:
InputLanguageChanged
Fired when the form's input language has been changed. Its syntax is:
CODES:
The InputLanguageChangedEventArgs class has three properties that identify the new language: CharSet, which defines the character set associated with the new input language; Culture, which contains the culture code (see Appendix C) of the new input language; and InputLanguage, which contains a value indicating the new language.

InputLanguageChanging
Fired when the form's input language is about to be changed. Its syntax is:
CODES:
The InputLanguageChangingEventArgs class has a Culture property that identifies the proposed new language and locale. It also has a Cancel property that can be set to True within the event handler to cancel the change of input language; the default value of the Cancel property is False.

Load
Fired when the form is loaded. Its syntax is:
CODES:
MaximizedBoundsChanged
Fired when the value of the form's MaximizedBounds property (which determines the size of
the maximized form) is changed. Its syntax is:
CODES:
MaximumSizeChanged
Fired when the value of the form's MaximumSize property (which defines the maximum size to
which the form can be resized) is changed. Its syntax is:
CODES:
MdiChildActivate
Fired when an MDI child window is activated. Its syntax is:
CODES:
MenuComplete
Fired when menu selection is finished. Its syntax is:
CODES:
MenuStart
Fired when a menu is displayed. Its syntax is:
CODES:
MinimumSizeChanged
Fired when the value of the form's MinimumSize property (which defines the minimum size to
which the form can be resized) is changed. Its syntax is:
CODES:
Relationships Between Forms
The Form class has two properties that control a form's relationship to other forms: the Parent property (inherited from the Control class) and the Owner property. Setting the Parent property causes the constrained form to appear only within the bounds of the parent—and always to appear on top of the parent. This gives an effect similar to MDI applications (which have other features as well and are discussed later in this chapter). When a form has a parent, it can be docked to the parent's edges, just like any other control. The code in Example 4-4 demonstrates this. It can be compiled from the command line with this command:
CODES:
The result is displayed in Figure 7.

Figure 7.
Example 4-4. Creating a form with a parent
CODES:

If the child form is maximized, it expands to fill the parent form. If the child form is minimized, it shrinks to a small rectangle at the bottom of the parent window. Because the child form in this example has a title bar and a sizable border, it can be moved and sized even though it has been docked. This behavior can be changed by modifying the form's FormBorderStyle property.

Setting the Owner property of a form causes another form to own the first. An owned form is not constrained to appear within the bounds of its owner, but when it does overlay its owner, it is always on top. Furthermore, the owned form is always minimized, restored, or destroyed when its owner is minimized, restored, or destroyed. Owned forms are good for floating-tool windows or Find/Replacetype dialog boxes. The code in Example 4-5 creates an owner/owned relationship. Compile it with this command:
CODES:
Example 4-5. Creating a form with an owner
CODES:


No comments:

Post a Comment