Windows Forms from Scratch


A typical Windows Forms application has at least one form. Without the form, it's just an "application," which is pretty boring. A form is simply a window, the unit of the Microsoft user interface we've seen since Windows 1.0.

One form in a Windows Forms application is typically the main form, meaning that it is either the parent or the owner of all other forms that may be shown during the lifetime of the application.[1] It's where the main menu is shown, along with the tool strip, status strip, and so on. Typically, the main form also governs the lifetime of a Windows Forms application; generally when an application starts, the main form is opened, and when the main form is closed, the application exits.

[1] The distinction between a form's "parent" and its "owner" is covered in detail in Chapter 2: Forms.

The main form of an application can be a simple message box, a dialog, a Single Document Interface (SDI) window, a Multiple Document Interface (MDI) window, or something more complicated (such as the forms you're used to seeing in applications like VS05). These latter forms may include multiple child windows, tool windows, and floating tool strips.

If your application is simple, you can implement it using the staple of any windowing system, the lowly message box:

class MyFirstApp {    static void Main() {      System.Windows.Forms.MessageBox.Show("Hello, Windows Forms!");    } }


If you're new to C#, Main is the entry point for any C# application.[2] The Main method must be a member of a class, and hence the need for MyFirstApp. However, the .NET runtime doesn't create an instance of the MyFirstApp class when our code is loaded and executed, so our Main method must be marked static. In this way, you mark a method as available without requiring the instantiation of the type that exposes it.

[2] The entry point is the method that the Common Language Runtime (CLR) calls when an application is launched. For details, refer to Essential .NET (Addison-Wesley, 2003), by Don Box, with Chris Sells.

The single line of real code in our first Windows Forms application calls the static Show method of the MessageBox class contained within the System.Windows.Forms namespace. Namespaces are used extensively in the .NET Framework Class Libraries (.NET Framework) to separate types such as classes, structures, and enumerations into logical groupings. This separation is necessary when you have thousands of Microsoft employees working on the .NET Framework, hundreds of third parties extending it, and millions of programmers trying to learn it. Without namespaces, you would need all kinds of wacky conventions to keep things uniquely named (as demonstrated by the existing Win32 API).

However, as necessary as namespaces are, they require a little too much typing for me, so I recommend the C# using statement:

using System.Windows.Forms; // For MessageBox class MyFirstApp {   static void Main() {     MessageBox.Show("Hello, Windows Forms!");   } }


When the compiler sees that the MessageBox class is being used, it first looks in the global namespace, which is where all types end up that aren't contained by a namespace (for example, the MyFirstApp class is in the global namespace). If the compiler can't find the type in the global namespace, it looks at all the namespaces currently being usedin this case, System.Windows.Forms. If the compiler finds a type name being used that exists in two or more namespaces, it produces an error and we're forced to go back to the long notation. In practice, this is rare enough to make the short notation predominant when you type code by hand.

However, even though the MessageBox class is enormously handy for showing your users simple string information it's hard to build a real application with MessageBox. For most things, you need an instance of the Form class, located in System.Windows.Forms:

using System.Windows.Forms; // For Form class MyFirstApp {   static void Main() {     Form form = new Form();     form.Show(); // Not what you want to do   } }


Although this code shows the form, you'll have to be quick to see it because the Show method displays the form modelessly. If you're not steeped in user interface lore, a modeless form is one that's displayed while allowing other activities (called modes) to take place. So, control is returned to the Main method immediately after Show puts our new form on the screen, which promptly returns and exits the process, taking our nascent form with it. To show a form modallythat is, to not return control to the Main function until the form has closedyou could call the ShowDialog method:

using System.Windows.Forms; class MyFirstApp {   static void Main() {     Form form = new Form();     form.ShowDialog(); // Still not what you want to do   } }


This code would show a blank form and wait for the user to close it before returning control to the Main method, but it's not the code you generally write. Instead, to make it accessible from other parts of your application, you designate one form as the main form. To do this, pass the main form as an argument to the Application object's static Run method, which also resides in the System.Windows.Forms namespace:

using System.Windows.Forms; // For Form, Application class MyFirstApp {   static void Main() {     Form form = new Form();     Application.Run(form); // This is what you want to do   } }


The Run method shows the main form. When the form is closed, Run returns, letting our Main method exit and close the process. To see this in action, you can compile your first Windows Forms application using the following command:[3]

[3] To get a command prompt with the proper PATH environment variable set to access the .NET command line tools, click on Start | Programs | Microsoft Visual Studio 2005 | Visual Studio Tools, and then Visual Studio 2005 Command Prompt. If you don't have VS05 installed, you can set up the PATH using the corvars.bat batch file in your FrameworkSDK\Bin directory.

C:\> csc.exe /t:winexe /r:System.Windows.Forms.dll MyFirstApp.cs


The csc.exe command invokes the compiler on our source file, asking it to produce a Windows application via the /t flag (where the "t" stands for "target"), pulling in the System.Windows.Forms.dll library using the /r flag (where the "r" stands for "reference").[4]

[4] csc.exe is the command line compiler for C#, and it is located in your c:\Windows\Microsoft.NET\Framework\v2.0.50727 folder.

The job of the compiler is to pull together the various source code files into a .NET assembly. An assembly is a collection of .NET types, code, or resources (or all three). An assembly can be either an application, in which case it has an .exe extension, or a library, in which case it has a .dll extension. The only real difference between assembly types is whether the assembly has an entry point that can be called by Windows when the assembly is launched (.exe files do, and .dll files do not).

Now that the compiler has produced MyFirstApp.exe, you can execute it and see an application so boring, it's not even worth a screen shot. When you close the form, MyFirstApp.exe exits, ending your first Windows Forms experience.

To spice things up a bit, we can set a property on our new form before showing it:

class MyFirstApp {   static void Main() {     Form form = new Form();     form.Text = "Hello, Windows Forms!";     Application.Run(form);   } }


Like most classes in the .NET Framework, Form has several properties to access, methods to call, and events to handle. In this case, we've set the Text property, which sets a form's caption bar text. We could do the same thing to set other properties on the form, showing it when we were finished, but that's not the way we generally do things in Windows Forms. Instead, each custom form is a class that derives from Form and initializes its own properties:

class MyFirstForm : Form {   public MyFirstForm() {     this.Text = "Hello, Windows Forms!";   } } class MyFirstApp {   static void Main() {     Form form = new MyFirstForm();     Application.Run(form);   } }


Notice that the MyFirstForm class derives from Form and then initializes its own properties in the constructor. This gives us a simpler usage model, as shown in the new Main method, which creates an instance of the MyFirstForm class. You also gain the potential for reuse should MyFirstForm be needed in other parts of your application.

Still, our form is pretty boring. It doesn't even include a way to interact with it except for the system-provided adornments. We can add some interactivity by adding a button:

class MyFirstForm : Form {   public MyFirstForm() {     this.Text = "Hello, Windows Forms!";     Button button = new Button();     button.Text = "Click Me!";     this.Controls.Add(button);   } }


Adding a button to the form is a matter of creating a new Button object, setting the properties that we like, and adding the Button object to the list of controls that the form manages. This code produces a button on the form that does that nifty 3-D depress thing that buttons do when you press them, but nothing else interesting happens. That's because we're still not handling the button's Click event, which is fired when the user presses the button:

using System; // For EventArgs ... class MyFirstForm : Form {   public MyFirstForm() {     this.Text = "Hello, Windows Forms!";     Button button = new Button();     button.Text = "Click Me!";     button.Click += new EventHandler(button_Click);     this.Controls.Add(button);   }   void button_Click(object sender, EventArgs e) {     MessageBox.Show("That's a strong, confident click you've got...");   } }


Handling the button's Click event involves two things. The first is creating a handler function with the appropriate signature; we've used the standard naming convention for events (VariableName_EventName) to name this method button_Click. The type signature of the vast majority of .NET events is a method that returns nothing and takes two parameters: an object that represents the sender of the event (our button, in this case) and an instance of either the EventArgs class or a class that derives from the EventArgs class.

The second thing that's needed to subscribe to an event in C# is shown by the use of the "+=" operator in the MyFirstForm constructor. This notation means that we'd like to add a function to the list of all the other functions that care about a particular event on a particular object, and that requires an instance of an EventHandler delegate object. A delegate is a class that translates invocations on an event into calls on the methods that have subscribed to the event.[5]

[5] Delegates and events are covered in depth in Appendix C: Delegates and Events.

For this particular event, we have the following delegate and event defined for us in the .NET Framework:

namespace System {    delegate void EventHandler(object sender, EventArgs e); } namespace System.Windows.Forms {   class Button {     public event EventHandler Click;   } }


Notice that the Click event on the Button class stores a reference to an EventHandler delegate. Consequently, to add our own method to the list of subscribers to the button's Click event, we create an instance of the EventHandler delegate. To achieve the same effect with less typing, C# offers a syntactic shortcut that allows you to simply provide the name of the subscribing method:

public MyFirstForm() {   ...   button.Click += button_Click;   ... }


Shortcut or not, it can quickly become tedious to add property settings and event handlers by hand for any nontrivial UI. Luckily, it's also unnecessary, thanks to the Windows Forms Application Wizard and the Windows Forms Designer provided by VS05.




Windows Forms 2.0 Programming
Windows Forms 2.0 Programming (Microsoft .NET Development Series)
ISBN: 0321267966
EAN: 2147483647
Year: 2006
Pages: 216

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net