Creating A Windows Form Application


The first thing you need to do is create a Windows Form application. For the following example, create a blank form and show it on the screen. This example does not use Visual Studio .NET. It has been entered in a text editor and compiled using the command-line compiler. Here is the code listing:

 using System; using System.Windows.Forms; namespace NotepadForms { public class MyForm : System.Windows.Forms.Form { public MyForm() { } [STAThread] static void Main() { Application.Run(new MyForm()); } } } 

When you compile and run this example, you will get a small blank form without a caption. Not real functional, but it is a Windows Form.

Looking at the code, two items deserve attention. The first is the fact that you have used inheritance to create the MyForm class. The following line declares that MyForm is derived fromSystem.Windows.Forms.

 public class MyForm : System.Windows.Forms.Form 

The Form class is one of the main classes in the System.Windows.Forms namespace. The other section of code that you want to look at is:

 [STAThread] static void Main() { Application.Run(new MyForm()); } 

Main is the default entry point into any C# client application. Typically in larger applications, the Main() method would not be in a form, but in a class that is responsible for any startup processing that needs to be done. In this case, you would set the startup class name in the project properties dialog box. Notice the attribute [STAThread]. This sets the COM threading model to single-threaded apartment (STA). The STA threading model is required for COM interop and is added by default to a Windows Form project.

The Application.Run() method is responsible for starting the standard application message loop. ApplicationRun() has three overloads: The first takes no parameter; the second takes an ApplicationContext object as a parameter; and the one you see in the example takes a form object as a parameter. In the example, the MyForm object will become the main form of the application. This means that when this form is closed, the application ends. By using the ApplicationContext class, you can gain a little more control over when the main message loop ends and the application exits.

The Application class contains some very useful functionality. It provides a handful of static methods and properties for controlling the application's starting and stopping process and to gain access to the Windows messages that are being processed by the application. The following table lists some of the more useful of these methods and properties.

Method/Property

Description

CommonAppDataPath

The path for the data that is common for all users of the application. Typically this is BasePath\Company Name\Product Name\Version where BasePath is C:\Documents and Settings\username\ ApplicationData. If it does not exist, the path will be created.

ExecutablePath

This is the path and file name of the executable file that starts the application.

LocalUserAppDataPath

Similar to CommonAppDataPath with the exception that this property supports roaming.

MessageLoop

True or false if a message loop exists on the current thread.

StartupPath

Similar to ExecutablePath, except the file name is not returned.

AddMessageFilter

Used to pre-process messages. By implementing an IMessageFilter- based object, the messages can be filtered from the message loop or special processing can take place prior to the message being passed to the loop.

DoEvents

Similar to the Visual Basic DoEvents statement. Allows messages in the queue to be processed.

EnableVisualStyles

Enables XP visual styles for the various visual elements of the appli- cation. There are two overloads that will accept manifest information. One is a stream of the manifest and the other is the full name and path of where the manifest exists.

Exit and ExitThread

Exit ends all currently running message loops and exits the applica- tion. ExitThread ends the message loop and closes all windows on the current thread.

Now what does this sample application look like when it is generated in Visual Studio 2005? The first thing to notice is that two files are created. The reason for this is that Visual Studio 2005 takes advantage of the partial class feature of the framework and separates all of the designer-generated code into a separate file. Using the default name of Form1, the two files are Form1.cs and Form1.Designer.cs. Unless you have the Show All Files option checked on the Project menu you won't see Form1.Designer.cs in the Solution Explorer. Following is the code that Visual Studio generates for the two files. First is Form1.cs:

 #region Using directives using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Windows.Forms; #endregion namespace VisualStudioForm { partial class Form1 : Form { public Form1() { InitializeComponent(); } } } 

This is pretty simple, a handful of using statements and a simple constructor. Here is the code in Form1.Designer.cs:

 namespace VisualStudioForm { partial class Form1 { /// <summary> /// Required designer variable. /// </summary> private System.ComponentModel.IContainer components = null; /// <summary> /// Clean up any resources being used. /// </summary> protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #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> private void InitializeComponent() { this.components = new System.ComponentModel.Container(); this.Text = "Form1"; } #endregion } } 

The designer file of a form should rarely be edited directly. The only exception would be if there is any special processing that needs to take place in the Dispose method. The InitializeComponent method is discussed later in this chapter.

Looking at the code as a whole for this sample application, you can see it is much longer than the simple command-line example. There are several using statements at the start of the class; most are not necessary for this example. There is no penalty for keeping them there. The class Form1 is derived from System.Windows.Forms just like the earlier notepad example, but things start to get different at this point. First there is this line in the Form1.Designer file:

 private System.ComponentModel.IContainer components = null; 

In the example, this line of code doesn't really do anything. When you add a component to a form, you can also add it to the components object, which is a container. The reason for adding to this container has to do with disposing of the form. The form class supports the IDisposable interface because it is implemented in the Component class. When a component is added to the components container, the container will make sure that the components are tracked properly and disposed of when the form is disposed of. You can see this if you look at the Dispose method in the code:

 protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } 

Here you can see that when the Dispose method is called, the Dispose method of the components object is also called and because the component object contains the other components, they are also disposed.

The constructor of the Form1 class, which is in the Form1.cs file, looks like this:

 public Form1() { InitializeComponent(); } 

Notice the call to InitializeComponent(). InitializeComponent() is located in Form1.Designer.cs and does pretty much what it describes, and that is to initialize any controls that might have been added to the form. It also initializes the form properties. This is what InitializeComponent() looks like for this simple example:

 private void InitializeComponent() { this.components = new System.ComponentModel.Container(); this.Text = "Form1"; } 

As you can see it is basic initialization code. This method is tied to the Designer in Visual Studio. When you make changes to the form by using the Designer, the changes are reflected in InitializeComponent(). If you make any type of code change in InitializeComponent(), the next time you make a change in the designer, your changes will be lost. InitializeComponent() gets regenerated after each change inthe designer. If you need to add additional initialization code for the form or controls and components on the form, be sure to add it after InitializeComponent() is called. InitializeComponent() is also responsible for instantiating the controls so any call that references a control prior to Initialize Component() will fail with a null reference exception.

To add a control or component to the form, press Ctrl+Alt-X or select Toolbox from the View menu in Visual Studio .NET. Form1 should be in design mode. Right-click Form1.cs in Solution Explorer and select View Designer from the context menu. Select the Button control and drag it to the form in the designer. You can also double-click the control and it will be added to the form. Do the same with the TextBox control.

Now that you have added a TextBox control and a Button control to the form, InitializeComponent() expands to include the following code:

 private void InitializeComponent() { this.button1 = new System.Windows.Forms.Button(); this.textBox1 = new System.Windows.Forms.TextBox(); this.SuspendLayout(); //  // button1 //  this.button1.Location = new System.Drawing.Point(96, 56); this.button1.Name = "button1"; this.button1.TabIndex = 0; this.button1.Text = "button1"; //  // textBox1 //  this.textBox1.Location = new System.Drawing.Point(88, 104); this.textBox1.Name = "textBox1"; this.textBox1.TabIndex = 1; this.textBox1.Text = "textBox1"; //  // Form1 //  this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); this.ClientSize = new System.Drawing.Size(292, 271); this.Controls.Add(this.textBox1); this.Controls.Add(this.button1); this.Name = "Form1"; this.Text = "Form1"; this.ResumeLayout(false); } 

If you look at the first three lines of code in the method, you can see the Button and TextBox controls are instantiated. Notice the names given to the controls, textBox1 and button1. By default the designer uses the name of the control and adds an integer value to the name. When you add another button,the designer adds the name button2, and so on. The next line is part of the SuspendLayout and ResumeLayout pair. SuspendLayout() temporarily suspends the layout events that take place whena control is first initialized. At the end of the method the ResumeLayout() method is called to set things back to normal. In a complex form with many controls, the InitializeComponent() method can get quite large.

To change a property value of a control, either press F4 or select Properties Window from the View menu. The Properties Window enables you to modify most of the properties for a control or component. By making a change in the Properties Window, the InitializeComponent() method will be rewritten to reflect the new property value. For example, if the Text property is changed to My Button in the Property Window, InitializeComponent() will contain this code:

 //  // button1 //  this.button1.Location = new System.Drawing.Point(96, 56); this.button1.Name = "button1"; this.button1.TabIndex = 0; this.button1.Text = "My Button"; 

If you are using an editor other than Visual Studio .NET, you will want to include an Initialize Component() type function in your designs. Keeping all of this initialization code in one spot will help keep the constructor cleaner, not to mention that if you have multiple constructors you can make sure the initialization code is called from each constructor.

Class Hierarchy

The importance of understanding the hierarchy becomes apparent during the design and construction of custom controls. If your custom control is a derivative of a current control, for example a text box with some added properties and methods, you will want to inherit from the text box control and then override and add the properties and methods to suit your needs. However, if you are creating a control that doesn't match up to any of the controls included with the .NET Framework, you will have to inherit from one of the three base control classes — Control or ScrollableControl if you need autoscrolling capabilities, and ContainerControl if your control needs to be a container of other controls.

Figure 23-1 shows the relationship between the classes of the System.Windows.Forms namespace.

image from book
Figure 23-1

The rest of this chapter is devoted to looking at many of these classes — how they work together and how they can be used to build professional-looking client applications.




Professional C# 2005
Pro Visual C++ 2005 for C# Developers
ISBN: 1590596080
EAN: 2147483647
Year: 2005
Pages: 351
Authors: Dean C. Wills

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