Getting started with Windows Forms
1.2 Adding controls
Let’s make our program a little more interesting by adding some controls. Through- out the course of the book, we will be building a photo viewing application, so let’s add a button for loading an image file, and a box where the image can be displayed. When we are done, our form will look like figure 1.3.
Revise your code as shown in listing 1.2. Changes from our previous code listing are shown in bold. Note that we have changed the version number of our program to 1.2
Figure 1.3
The main window shown here con- tains a Load button and a picture box control.
to distinguish it from our original code and to match the current section. This new version number is also displayed on the title bar. In chapter 2 we will see how to obtain the application’s version number programmatically. For now, changing it by hand will do just fine.
[assembly: System.Reflection.AssemblyVersion("1.2")] namespace MyNamespace
{
using System;
using System.Windows.Forms;
public class MyForm : Form {
private Button btnLoad;
private PictureBox pboxPhoto;
public MyForm() {
this.Text = "Hello Form 1.2"; // Create and configure the Button
btnLoad = new Button(); btnLoad.Text = "&Load"; btnLoad.Left = 10; btnLoad.Top = 10;
// Create and configure the PictureBox
pboxPhoto = new PictureBox(); pboxPhoto.BorderStyle =
System.Windows.Forms.BorderStyle.Fixed3D; pboxPhoto.Width = this.Width / 2; pboxPhoto.Height = this.Height / 2;
pboxPhoto.Left = (this.Width - pboxPhoto.Width) / 2; pboxPhoto.Top = (this.Height - pboxPhoto.Height) / 2; // Add our new controls to the Form
this.Controls.Add(btnLoad); this.Controls.Add(pboxPhoto);
}
public static void Main() {
Application.Run(new MyForm()); }
} }
Compile this program as before and run it to see our changes. We will walk through these changes one at a time.
ADDINGCONTROLS 15
1.2.1 Shortcuts and fully qualified names
The first change you may notice in our new code is the using keyword at the begin-
ning of the program.
using System;
using System.Windows.Forms;
Programmers are always looking for shortcuts; and older programmers, some would say more experienced programmers, often worry that their lines may be too long for the compiler or printer to handle. The programmers at Microsoft are no exception, so while one team probably agreed that fully-qualified names are a good idea, another team probably sought a way to avoid typing them. The result is the using keyword.
The using keyword actually plays two roles in C#. The first is as a directive for
specifying a shortcut, as we are about to discuss. The second is as a statement for ensur- ing that non-memory resources are properly disposed of. We will discuss the using
keyword as a statement in chapter 6.
As a directive, using declares a namespace or alias that will be used in the cur-
rent file. Do not confuse this with include files found in C and C++. Include files are not needed in C# since the assembly incorporates all of this information, making the /reference switch to the compiler sufficient in this regard. This really is just
a shortcut mechanism.
In our original program in section 1.1, the Main function called the method System.Windows.Forms.Application.Run. In our new listing the using
directive allows us to shorten this call to Application.Run. The long form is called
the fully qualified namesince the entire namespace is specified. Imagine if you had to use the fully qualified name throughout your code. Aside from tired fingers, you would have long, cluttered lines of code. As a result, our new code is a bit easier to read:
public static void Main() {
Application.Run(new MyForm()); }
Since Application is not a C# keyword or a globally available class, the compiler
searches the System and System.Windows.Forms namespaces specified by the using directive in order to locate the System.Windows.Forms.Application
class.
You can also specify an alias with the using keyword to create a more convenient
representation of a namespace or class. For example,
using WF-alias = System.Windows.Forms
With this alias defined, you can then refer to the Applicationclass as
Alternatively, an alias for a specific type can be created. For example, a shortcut for the Application class can be defined with:
using MyAppAlias = System.Windows.Forms.Application
This would permit the following line in your code:
MyAppAlias.Run(new MyForm());
Typically, the using directive simply indicates the namespaces employed by the
program, and this is how we use this directive in our program. For example, rather
than the fully qualified names System.Windows.Forms.Button and Sys-
tem.Windows.Forms.PictureBox, we simply use the Button and PictureBox
names directly.
It is worth noting that there is also a Button class in the System.Web.UI.Web- Controlsnamespace. The compiler uses the correct System.Windows.Forms.But- ton class because of the using keyword, and because the System.Web namespace
is not referenced by our program.
When we look at Visual Studio .NET in chapter 2, you will see that Visual Studio tends to use the fully qualified names everywhere. This is a good practice for a tool that generates code to guarantee that any potential for ambiguity is avoided.
1.2.2 Fields and properties
Let’s go back to our use of the Button and PictureBox classes. The top of our class
now defines two member variables, or fields in C#, to represent the button and the picture box on our form. Here, Button and PictureBox are classes in the Win-
dows Forms namespace that are used to create a button and picture box control on a
Form. We will tend to use the terms class and control interchangeably for user inter-
face objects in this book.3
public class MyForm : Form {
private Button btnLoad; private PictureBox pboxPhoto;
Fields, like all types in C#, must be initialized before they are used. This initialization occurs in the constructor for the MyForm class.
public MyForm() {
// Create and configure the Button btnLoad = new Button();
btnLoad.Text = "&Load"; btnLoad.Left = 10; btnLoad.Top = 10;
3 Or, more formally, we will use the term control to refer to an instance of any class derived from the
ADDINGCONTROLS 17
// Create and configure the PictureBox pboxPhoto = new PictureBox();
pboxPhoto.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; pboxPhoto.Width = this.Width / 2;
pboxPhoto.Height = this.Height / 2;
pboxPhoto.Left = (this.Width - pboxPhoto.Width) / 2; pboxPhoto.Top = (this.Height - pboxPhoto.Height) / 2; . . .
Note the use of the new keyword to initialize our two fields. Each control is then
assigned an appropriate appearance and location. You might think that members such as Text,Left, BorderStyle, and so onare all public fields in the Button
and PictureBox classes, but this is not the case. Public member variables in C++, as
well as in C#, can be a dangerous thing, as these members can be manipulated directly by programmers without restrictions. A user might accidentally (or on pur- pose!) set such a variable to an invalid value and cause a program error. Typically, C++ programmers create class variables as protected or private members and then provide public access methods to retrieve and assign these members. Such access methods ensure that the internal value never contains an invalid setting.
In C#, there is a class member called properties designed especially for this pur- pose. Properties permit controlled access to class fields and other internal data by pro- viding read, or get, and write, or set, access to data encapsulated by the class.
Examples later in the book will show you how to create your own properties. Here we use properties available in the Button and PictureBox classes.4
We have already seen how the Text property is used to set the string to appear
on a form’s title bar. For Buttonobjects, this same property name sets the string that
appears on the button, in this case “&Load.” As in previous Windows programming environments, the ampersand character ‘&’ is used to specify an access key for the con-
trol using the Alt key. So typing Alt+L in the application will simulate a click of the Load button.
Windows Forms controls also provide a Left, Right, Top, and Bottomprop- erty to specify the location of each respective side of the control. Here, the button
is placed 10 pixels from the top and left of the form, while the picture box is centered on the form.
The Width and Heightproperties specify the size of the control. Our code cre-
ates a picture box approximately 1/2 the size of the form and roughly centered within it. This size is approximate because the Width and Height properties in the Form
class actually represent the width and height of the outer form, from edge to edge.5 4 As we will see in later chapters, the properties discussed here are inherited from the Control class. 5 The ClientRectangle property represents the size of the internal display area, and could be used
1.2.3 The Controls property
The final lines in the MyForm constructor add the button and picture box controls to
the form using the Controls property. The Controls property returns an instance
of the Control.ControlCollection class. The ControlCollection class is
defined within the Form class, and defines an Add method that adds a control to a
form. Note that the Controls property can be used to retrieve the controls on a
form as well.
public MyForm() {
. . .
// Add our new controls to the Form this.Controls.Add(btnLoad);
this.Controls.Add(pboxPhoto); }
When a control is added to a form, it is placed at the end of the z-order of the stack of controls on the form. The term z-order is used for both the set of forms in the appli- cation and the set of controls on a particular form, and indicates the order of win- dows stacked on the screen or controls stacked on a form, much like stacking dishes on a table.
The end of the z-order is bottom of the stack. You can think of this as the view a chandelier has of a table. If the tabletop is the form, and a cup and saucer are con- trols, in your code you would first add the cup control to the table, then add the saucer control so that it appears underneath the cup. This can be a bit unintuitive, so make sure you understand this point when programmatically adding controls to your forms. The term z-order comes from the fact that the screen is two-dimensional, and is often treated as a two-axis coordinate system in the X and Y directions. The imaginary axis perpendicular to the screen is called the z-axis. This concept of z-order will be important later in the chapter when we have overlapping controls.
Now that our controls are placed on the form, we can use them to load and dis- play an image.