|
Chapter 2: Hello, Windows Forms continued
Variations on a ThemeLet's try creating two forms to get a better feel for this process.TwoForms.vb
'----------------------------------------- This program creates two forms, named frm1 and frm2, and gives them two different caption texts so that you can tell them apart. The Show method is called for frm2, and frm1 is passed to Application.Run. A message box indicates when Application.Run returns control back to Main. You may want to run TwoForms a couple times to see what's going on. If you close frm2 first, frm1 is unaffected. The only way you can get Application.Run to return and the program to display its message box is to also close frm1. If you close frm1 first, however, both forms disappear from the screen, Application.Run returns control to Main, and the message box is displayed. So that's something else that Application.Run does: when you close the form passed as an argument to Application.Run, the method closes all the other forms created by the program. If you don't pass a Form object to Application.Run (as RunFormBadly demonstrated), the program needs to explicitly call the Application.Exit method to force Application.Run to return. But where can the program call Application.Exit if it's off somewhere in the Application.Run call? We'll see shortly how a program can set events that return control to a program and potentially give it the opportunity to call Application.Exit if it needs to.
Form PropertiesLike many other classes, the Form class defines a number of properties, and Form also inherits additional properties from its ancestors, particularly Control. Two such properties that I've already described are Text and Visible. Here's a program that sets a smattering of sample properties to illustrate some of the flexibility you have in creating and displaying a form.FormProperties.vb
'----------------------------------------------- BackColor is the property that determines the background color of the form. As you'll see in the next chapter, Color is a structure defined in the System.Drawing namespace (notice the Imports statement) that contains 141 properties that are actually color names. These names are listed on the inside back cover of this book. The Width and Height properties determine the initial dimensions of the form. The two statements that change these properties perform both Get and Set operations, effectively doubling the width of the window and halving its height from the default values. FormBorderStyle is an enumeration that defines not just the appearance and functionality of the form's border but other aspects of the form as well. Here are the possible values: FormBorderStyle Enumeration
The default FormBorderStyle.Sizable style results in a form that has a caption bar with a control box on the left, followed by the caption bar text; and a minimize box, a maximize box, and a close box at the right. A tool window has a shorter caption bar, no control box, no minimize box, and no maximize box. The FormBorderStyle.FixedSingle style I've used in this program prevents the user from resizing the form. In addition, I've set the MaximizeBox property to False, so the maximize box is disabled, as shown here: The Cursor property indicates what the mouse cursor looks like when it's moved to the client area of the form. The StartPosition property indicates where the form is initially displayed; the FormStartPosition enumeration value CenterScreen directs the form to appear in the center of the screen rather than in a default position determined by Windows. As you look at the FormProperties program, you might start to be puzzled about how real-life Windows Forms programs are structured. It seems like you need to call Application.Run to get the form to interact with the user, but Application.Run doesn't return until the form is closed. In short, there doesn't seem to be any place to put your code!
Event-Driven InputMany console programs don't interact with a user at all. A typical console application obtains all the information it needs from command-line arguments, does its stuff, and then terminates. If a console program needs to interact with a user, it gets input from the keyboard. In the .NET Framework, a console program reads keyboard input by calling the Read or ReadLine methods of the Console class. After the program pauses to get keyboard input, it then continues on its way.Programs written for graphical environments, however, have a different input model. One reason for this is the existence of multiple input devices. Programs get interactive input not only from the keyboard but also from the mouse. In addition, programs can create controlssuch as buttons, menus, and scroll barsthat also interact with the user on behalf of the main program. In theory, I suppose, a programming environment that supported multiple input devices could handle everything using the technique of serial polling. In serial polling, the program checks for input from the keyboard, and if there is none, checks the mouse; if there's none there, it checks for input from the menu, and the menu checks for input from the keyboard and the mouse, and so forth. (Prior to the advent of Windows, character-mode PC programs that used mouse input generally implemented serial polling.) It turns out, however, that a better input model for multiple input devices is the event-driven model. As implemented in Windows Forms, each type of input is associated with a different method in your program. When a particular input event occurs (such as a key on the keyboard being pressed, the mouse being moved, or an item being selected from the program's menu), the appropriate method is called, seemingly from outside the program. At first, this input model sounds chaotic. As the user is typing away and moving the mouse, pressing buttons, scrolling scroll bars, and picking menu selections, the program must get bombarded with method calls coming from all different directions. Yet in practice, it's much more orderly than it sounds because all the methods exist in the same execution thread. Events never interrupt a program's execution. Only when one method finishes processing its event is another method called with another event. Indeed, after a Windows Forms program performs initialization on its form, everything that the program doesevery little piece of code it executesis in response to an event. For much of the time, the program is sitting dormant, somewhere deep inside the Application.Run call, waiting for an event to happen. Indeed, it's often helpful to think of your Windows Forms programs as state machines whose state is determined entirely by changes initiated by events. Events are so important that they are woven into the very fabric of the .NET Framework and Visual Basic .NET. Events are members of classes along with constructors, fields, methods, and properties. When a program defines a method to process an event, the method is called an event handler. The arguments of the handler match a function prototype definition called a delegate. We'll see how this all works shortly. As you'll discover in Chapter 6, there are three different types of keyboard events. One type of event tells you when a key is pressed and another when the key is released. A third keyboard event tells you when a character code has been generated by a particular combination of keystrokes. In Chapter 8, I'll introduce the seven types of mouse events, indicating when the mouse has moved and what buttons have been clicked or double-clicked. In Chapter 10, you'll see that there's also a timer event. This event periodically notifies your form when a preset length of time has elapsed. Clock programs use timer events to update the time every second. In Chapter 12, when we start creating controls (such as buttons and text boxes and list boxes) and putting them on the surface of forms, you'll find out that these controls communicate information back to the form with events. Events indicate when the button has been clicked or the text in the text box has changed. In Chapter 14, you'll discover that menus also communicate information to a form using events. There's an event to indicate when a drop-down menu is about to be displayed, an event to indicate when a menu item is selected, and an event to indicate when a menu item is clicked. But one of the oddest eventsperhaps the most unlikely candidate for eventhoodis also one of the most important. This event, known as the Paint event, tells your program when you need to display output on your window. Nothing reveals the enormous difference between command-line programs and graphical programs more than the Paint event. A command-line program displays output whenever it feels like it. A Windows Forms program can display output whenever it wants to as well, but doing so isn't quite adequate. What the Paint event is really doing is informing a program when part or all of the form's client area is invalid and must be redrawn. How does a client area become invalid? When a form is first created, the entire client area is invalid because the program hasn't yet drawn anything. The first Paint event that a program receives tells the program to draw something on the client area. When you move windows around the screen so that they overlap, Windows doesn't save the appearance of a client area that is covered by another window. When that client area is later uncovered, the program must restore its appearance. For that reason, it gets another Paint event. When you restore a program that's been minimized, you get another Paint event. A Windows program must be able to entirely repaint its client area at any time. It must retainor keep quickly accessibleall the information it needs to do this. Structuring your programs to respond properly to Paint events may sound quite restrictive, but you'll get the hang of it.
Handling the Paint EventThe subject of events is best approached with examples. In practical terms, handling a Paint event in your program first involves taking a look at PaintEventHandler, a delegate that is defined in the System.Windows.Forms namespace with a single statement that (in Visual Basic syntax) looks something like this:
Public Delegate Sub PaintEventHandler(ByVal obj As Obje ct, _ If this statement looks like a function prototype to you, you're not too far from the mark. The second argument indicates a class named PaintEventArgsalso defined in the System.Windows.Forms namespacethat I'll discuss shortly. To handle Paint events in one of the programs shown earlier in this chapter, you must define a method in your module that has the same arguments and return type as the PaintEventHandler delegate:
Sub MyPaintHandler(ByVal obj As Object, ByVal pea As Pa intEventArgs) You then attach this event handler to the Paint event of the Form object using the Visual Basic AddHandler statement
AddHandler frm.Paint, New PaintEventHandler(AddressOf M yPaintHandler) or the simpler syntax that I'll be using in this book:
AddHandler frm.Paint, AddressOf MyPaintHandler Paint is an event defined in the Control class and is part of the Form class by virtue of inheritance. The only two operations you can perform on the Paint event involve the AddHandler and RemoveHandler statements. The AddHandler statement installs an event handler by attaching a method to an event. The general syntax is
AddHandler object.event, AddressOf method You detach a method from an event by using the same general syntax but with RemoveHandler:
RemoveHandler object.event, AddressOf method Detaching a method from an event is rarely necessary, however. Generally, you'll install an event handler and never uninstall it. The two arguments to the Paint event handler are an object I've called obj and a PaintEventArgs class I've abbreviated as pea. The first argument refers to the object that this Paint event applies to, in this case, the object frm. The object is sometimes called a "sender" because the event originates from that object. The PaintEventArgs class is defined in the System.Windows.Forms namespace, and it has two properties, Graphics and ClipRectangle, which are both read-only: PaintEventArgs Properties
The Graphics property contains an instantiation of the Graphics class, which is defined in the System.Drawing namespace. Graphics is an extremely important class in the Windows Forms library, ranking right up there with Form. This is the class you use to draw graphics and text on your form. The System.Drawing namespace implements a graphics programming system known as GDI+, which is an enhanced version of the Windows Graphics Device Interface. I'll discuss the ClipRectangle property in Chapter 4. In a vast majority of the programs in this book, you'll see
Dim grfx As Graphics = pea.Graphics as the first line in the Paint event handler. You can name your Graphics object whatever you want. This object shows up so much in graphics code that some programmers use just the letter g for it! I've taken a more moderate approach. Before all this new stuff piles up too deeply, let's take a look at an actual program that implements a Paint event handler. PaintEvent.vb
'------------------------------------------- After the form is created in Main, the method named MyPaintHandler is attached to the Paint event of the form. In this handler, the program obtains a Graphics object from the PaintEventArgs class and uses that to call the method Clear. Clear is a simple methodperhaps the simplest drawing methoddefined in the Graphics class: Graphics Methods (selection)
The argument is an object of type Color, which I'll discuss in much more detail in the next chapter. As I mentioned in connection with the FormProperties program shown earlier in this chapter, the easiest way to get a color is to specify one of the 141 color names implemented as shared properties in the Color structure. To get an idea of the frequency with which the program gets Paint events, try inserting the statement
Console.WriteLine("Paint Event")in MyPaintHandler. A couple programs in the next chapter will also visually demonstrate the frequency of Paint events. From here on, all the Windows Forms programs in this book will have at least the following three Imports statements at the top of the program:
Imports System Generally, these are the minimum required for any nontrivial Windows Forms application. You might see a connection between these three Imports statements and the three DLLs that you need to specify as references when compiling the program. They're certainly related but they're not precisely the same. The Imports statements are somewhat similar to the With statement in Visual Basic. They exist solely so that you don't have to type fully qualified class names. The DLLs specified as references provide the Visual Basic compiler with all the information about the classes, methods, properties, and so forth implemented in the DLLs. These same DLLs are later linked with the running program to implement these classes. Some programs later in this book have an Imports statement:
Imports System.Drawing.Drawing2D This is a namespace consisting of classes and other items also located in the dynamic-link library System.Drawing.dll.
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||