Windows Forms in Visual Studio .NET






Windows Forms in Visual Studio .NET

Most Windows Forms projects start in the New Project dialog, available via File | New | Project (Ctrl+Shift+N) and shown in Figure.

1. Windows Forms Projects


To develop an application, you want the Windows Application project template. To develop a library of custom controls or forms for reuse, you want the Windows Control Library project template. When you run the Windows Application Wizard, choosing whatever you like for the project name, location, and solution name, click OK and you'll get a blank form in the Windows Forms Designer, as shown in Figure.

2. A Windows Forms Application Wizard-Generated Blank Form in the Windows Forms Designer


Before we start the control drag-and-drop extravaganza that the Windows Forms Designer enables, let's look at a slightly abbreviated version of the code generated by the Windows Forms Application Wizard (available by right-clicking on the design surface and choosing View Code or by pressing F7):[6]

[6] The Windows Forms Designer offers two form views: Code and Designer. F7 toggles between them (although this keyboard shortcut is merely the VS05 default and, like any keystroke, depends on your specific settings).

// Program.cs
using System.Windows.Forms;

namespace MySecondApp {
  static class Program {
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main() {
      Application.EnableVisualStyles();
      Application.SetCompatibleTextRenderingDefault(false);
      Application.Run(new Form1());
    }
  }
}

// Form1.cs
using System.Windows.Forms;
namespace MySecondApp {
  partial class Form1 : Form {
    public Form1() {
      InitializeComponent();
    }
  }
}

// Form1.Designer.cs
namespace MySecondApp {
  partial class Form1 {

    #region Windows Form Designer generated code

    /// <summary>
    /// Required method for Designer support - do not modify
    /// the contents of this method with the code editor.
    /// </summary>
    void InitializeComponent() {
      this.components = new System.ComponentModel.Container();
      this.AutoScaleMode = AutoScaleMode.Font;
      this.Text = "Form1";
    }

    #endregion
  }
}

Most of this code should be familiar, including the using statements at the top, the form class that derives from the Form base class, the static Main function that provides the entry point to the application, and the Application.Run method. However, four things differ from what we did ourselves.

First, the Windows Forms Designer has dynamic theme support because of the call to Application.EnableVisualStyles, which keeps a UI's appearance consistent with the current Windows theme.

Second, the Windows Forms Designer has also set the default form's AutoScaleMode property to a value of AutoScaleMode.Font, which ensures that the form will automatically retain the correct visual proportions (as discussed in Chapter 4: Layout).

Third, the static Main method is implemented from a static class, Program, which exists in a file, program.cs, that's separate from any of the UI elements in the application. Main is augmented with the STAThread attribute, which enables appropriate communication between Windows Forms and Component Object Model (COM) technology. This is required for several types of Windows Forms functionality, including using the Clipboard, the file dialogs, and drag and drop (shown in Appendix E: Drag and Drop). Because any serious Windows Forms application likely uses some form of COM, the Windows Forms Designer tacks this on to protect you from nasty exceptions that would otherwise arise.

Finally, a call to InitializeComponent is added to the form's constructor to set the form's properties instead of doing it in the constructor itself. InitializeComponent gives the Windows Forms Designer a place to put the code to initialize the form and its controls and components as we visually design the form. For example, dragging a button from the Toolbox onto the form's design surface changes the InitializeComponent implementation to the following, in its entirety:

void InitializeComponent() {
  this.button1 = new System.Windows.Forms.Button();
  this.SuspendLayout();
  //
  // button1
  //
  this.button1.Location = new System.Drawing.Point(205, 75);
  this.button1.Name = "button1";
  this.button1.Size = new System.Drawing.Size(75, 23);
  this.button1.TabIndex = 0;
  this.button1.Text = "button1";
  this.button1.UseVisualStyleBackColor = true;
  //
  // Form1
  //
  this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
  this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
  this.ClientSize = new System.Drawing.Size(292, 266);
  this.Controls.Add(this.button1);
  this.Name = "Form1";
  this.Text = "Form1";
  this.ResumeLayout(false);
}

Notice again that this code is similar to what we built ourselves, although this time created for us by the Windows Forms Designer. Unfortunately, for this process to work reliably, the Windows Forms Designer must have complete control over the InitializeComponent method. In fact, you can notice from the previous sample that the Wizard-generated InitializeComponent code is wrapped in a region that is collapsed to hide the code by default, and is marked with a telling comment:

#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
...
#endregion

To emphasize the need for control, the Windows Forms Designer splits the Form1 class across two filesForm1.cs and Form1.Designer.csusing partial class support in C#.

The code in InitializeComponent may look like your favorite programming language, but it's actually the serialized form of the object model that the Windows Forms Designer uses to manage the design surface. Although you can make minor changes to this code, such as changing the Text property on the new button, major changes are likely to be ignoredor, worse, thrown away. Feel free to experiment to find out how far you can go by modifying this serialization format by hand, but don't be surprised if your work is lost. I recommend putting custom form initialization into the form's constructor, after the call to InitializeComponent, giving you confidence that your code will be safe from the Windows Forms Designer.

However, we put up with the transgression of the Windows Forms Designer because of the benefits it provides. For example, instead of writing lines of code to set properties on the form or the controls contained therein, all you have to do is to right-click on the object of interest and choose Properties (or press F4) to bring up the Properties window for the selected object, as shown in Figure.[7]

[7] Instead of F4, you can press Alt+Enter.

3. Browsing and Editing Properties in the Properties Window


Any properties with nondefault values, as indicated by values in boldface in the browser, are written to the InitializeComponent method for you. Similarly, to choose an event to handle for the form, or a control or component hosted on the form, you can press the Events lightning bolt button at the top of the Properties window to open the corresponding list of events (shown in Figure).

4. Creating Events with the Properties Window


You have a few ways to handle an event from the Properties window. One way is to find the event you'd like to handle on the object selected (say, Click), type the name of the function you'd like to call when this event is fired (say, button_Click), and press Enter. VS05 takes you to the body of an event handler with that name and the correct signature, all ready for you to implement:

void button_Click(object sender, System.EventArgs e) {
}

After you've added a handler to a form, that handler will show up in a drop-down list for other events having the same signature. This technique is handy if you'd like the same event for multiple objects to be handled by the same method, such as multiple buttons with the same handler. You can use the sender argument to determine which object fired the event:

void button_Click(object sender, System.EventArgs e) {
  Button button = sender as Button;
  MessageBox.Show(button.Text + "was clicked");
}

If you'd like each event that you handle for each object to be unique or if you just don't care what the name of the handler is, as is often the case, you can simply double-click on the name of the event in the Properties window; an event handler name is generated for you, based on the name of the control and the name of the event. For example, if you double-clicked on the Load event for the Form1 form, the event handler name would be Form1_Load.

Furthermore, if you want to handle the default event of an object, you can create a handler for it automatically by simply double-clicking on the object itself. This generates an event handler name just as if you'd double-clicked on that event name in the Properties window event list. An object's default event is intuitively meant to be the most handled event for a particular type. For example, I'm sure you won't be surprised to learn that the default event for a button is Click and that the default event for a form is Load. Unfortunately, neither the Windows Forms Designer nor the Properties window gives any indication what the default event is for a particular type, but experimentation should reveal few surprises.



 Python   SQL   Java   php   Perl 
 game development   web development   internet   *nix   graphics   hardware 
 telecommunications   C++ 
 Flash   Active Directory   Windows