Windows Forms

 
Chapter 7 - Windows Applications
bySimon Robinsonet al.
Wrox Press 2002
  

Almost every Windows Forms application extends the functionality of System.Windows.Forms . The basic functionality of the Form class doesn't accomplish much more than to create a window that can live and interact in the Windows environment correctly. This is useful as a starting point, and by extending the Form class and adding custom controls and custom event handlers, a very useful application can be created that can interact with the user and present data through a sophisticated user-interface .

We are going to examine how this process works in two ways. To better understand how Windows Forms operates and how it interacts with the .NET Framework, we will first build a complete Windows application without the use of Visual Studio .NET. This should provide you with a healthy appreciation of Visual Studio .NET when we then move to building Windows Forms applications using it. VS .NET allows developers to create Windows Forms application faster and more efficiently than they can by hand.

Windows Forms Without Visual Studio .NET

Again, almost every Windows Forms application will extend the System.Windows.Forms class to customize and add business logic. Therefore, the simplest Windows Forms application would be the following:

   using System;     using System.Windows.Forms;     namespace WindowsFormsApp     {     class MyForm : Form     {     static void Main(string[] args)     {     MyForm aForm = new MyForm();     Application.Run(aForm);     }     }     }   

To see this in action, save the above code as BasicForm.cs , then compile and run it. Alternatively, this file can be found in the code download for this book in the BasicForm folder, under the WindowsApplications directory. You should see the following output:

click to expand

When the application is run (by double-clicking on the executable that has been output by the compiler) a very basic window will be opened. Note that the window acts like a standard window and can be minimized, maximized, dragged, and closed. Not the next killer app, but it is a fully functional Windows application in 13 lines of code. Let's take a look at the code itself to understand what is happening before we move on to more exciting things.

 class MyForm : Form 

This line shows that our class is deriving from the System.Windows.Forms.Form class, meaning we gain access to all the functionality of the basic Form class. Next, note that in the Main() method (which is the standard Main() method we have used for all .NET applications so far) we create an instance of the custom MyForm object and pass it to the Application.Run() method:

 static void Main(string[] args)       {   MyForm aForm = new MyForm();     Application.Run(aForm);   } 

Application is a static class available in the System.Windows.Forms namespace that contains methods to start and stop applications and threads. The Run() method can accept several parameters; by passing in a Form object we are signaling to the .NET Framework to begin processing Windows messages for this form , and to exit the application when this form closes .

Controls

Let's add a simple Button control to the form. We will cover events much more extensively later in the chapter; for now we will only examine what it takes to add a control to a .NET Windows Forms application without Visual Studio .NET.

Basically every control on a form is a data member of the custom Form class. Therefore, to add a button to our form we must add a new Button data member to the MyForm class. Add the following line to the BasicForm.cs file:

 class MyForm : Form    {   //Data member to hold Button control     private Button BigButton;   

Before this data member will do anything or display a button on the form it must be initialized and the Button's various properties must be configured. This should be done in the constructor for the MyForm object. At this time we will also set the properties for the Form object itself, such as the size and name . Note that there are many properties that can be set, and doing so in the constructor is the best time to do so for initial values. Add the following block of code to the constructor of MyForm :

 public MyForm()       {   //Set the properties for the Button     BigButton = new Button();     BigButton.Location = new System.Drawing.Point(50, 50);     BigButton.Name = "BigButton";     BigButton.Size = new System.Drawing.Size(100, 100);     BigButton.Text = "Click Me!";     //Set properties of the Form itself     ClientSize = new System.Drawing.Size(200, 200);     Controls.Add(BigButton);     Text = "My Windows Form!";   } 

This code first instantiates a new Button object and assigns it to the private data member BigButton . It then sets the Location , Name , Size, and Text properties to appropriate values. Any properties not set here will retain their default settings, which vary depending on the property. For example, the BackColor property will remain the standard Windows button color .

The next lines set the size of the form itself, and then the this.Controls.Add() method is called to actually add the Button control to the Controls collection of the form . This is required before the button will actually be displayed on the form . The Controls collection contains all of the controls on a form , and can be dynamically updated and edited at runtime in order to add and remove controls as needed. We will be examining how this can be accomplished later in the chapter.

If you run the application at this point, you will see a slightly more exciting window:

However, nothing happens when the button is clicked. To change this we will need to add an event handler to the code.

Events

Recall our discussion of delegates and events from developer to write code that responds to user interface and system events. Every object in a Windows Forms application has a set of events that can be responded to. These are all optional  the .NET Framework will take care of all of the basic plumbing required to make windows open and close, buttons press and depress, menus to draw themselves , and so on. However, if you want to have some code that actually does something useful when these events occur, you need to add an event handler to the class and associate it with the object.

For Windows Forms to utilize your custom code, you must give it the location of the event handler method in your code. You do this by creating an appropriate delegate instance associated with a method in the custom Form class. Later in the chapter we will explore how to throw our own events when we expose events from a custom control.

To add some functionality behind that pesky button we need to add some code to our class. Add the following method to our Form class. This will act as the event handler for the button's Click event. Note that the event handler can be called anything. The control's event itself defines the parameters expected by the handler.

 static void Main(string[] args)       {          MyForm aForm = new MyForm();          Application.Run(aForm);       }   private void ClickHandler(object sender, System.EventArgs e)     {     MessageBox.Show("Clicked!","My Windows Form",MessageBoxButtons.OK);     }   

Most Windows Forms event handlers have exactly this method signature. The first parameter contains the object that raised the event. In this case it will be the Button object from the MyForm class. The next parameter contains data about the event in a System.EventArgs parameter or derived class. The System.EventArgs class actually contains no data  it only acts as a base class. If an event must pass data to the client it must utilize a derived class. The Button.Click event does not need to pass any additional information, so it utilizes the base System.EventArgs class. There are a huge number of derived classes that each contain information specific to the event they are tied to. We will cover some of these classes, but it is not possible to cover all of them in this chapter.

Finally, add the following code to the MyForm constructor to attach our event handler to the event in the MyForm class.

 public MyForm() {    //Set the properties for the Button    BigButton = new Button();    BigButton.Location = new System.Drawing.Point(50, 50);    BigButton.Name = "BigButton";    BigButton.Size = new System.Drawing.Size(100, 100);    BigButton.Text = "Click Me!";   BigButton.Click += new EventHandler(ClickHandler);   //Set properties for the Form itself    ClientSize = new System.Drawing.Size(200, 200);    Controls.Add(BigButton);    Text = "My Windows Form!"; } 

This example shows how Windows Forms uses delegates to wrap an object's method before assigning it to the event we want to handle. The System.EventHandler delegate is used to reference the ClickHandler() method and it is associated with the button's Click event by simply adding it to the Click event handler chain. Note the syntax used  this means additional event handlers can be associated with a single event. They will be processed in the order they were added to the event handler.

Compile this application again, just like we did before, and run it. This time when the button is clicked you should see a small MessageBox giving you some feedback:

click to expand

Now that we have seen how simple Windows Forms applications are structured in .NET, let's take a look at how Visual Studio .NET makes creating them even easier.

Windows Forms Using Visual Studio .NET

Like many things in .NET, using Visual Studio .NET makes writing Windows Forms applications much simpler. Visual Studio .NET reduces the amount of plumbing code developers must write themselves, allowing developers to concentrate on solving the business problem.

Let's jump right into this and see how to create a simple Windows Forms application using Visual Studio .NET. We are going to create a simple data entry screen for a fictitious personnel information management system. This type of screen would most likely be attached to some form of database used to store personnel data. We will wait to discuss data access issues until later in the book, but we will examine how to create the user-interface layer in this chapter. This project can be found in the online code in the SimpleDataEntry directory if you don't want to build the example yourself. Create a new C# Windows Application project in Visual Studio .NET titled SimpleDataEntry .

click to expand

After the project is created, you will see a simple form in Visual Studio .NET in the design view. The design view is used to visually add controls to the form , much like the Visual Basic 6 IDE. Right-click on the Form1.cs file in the Solution Explorer and select View Code . This will display the code that generates the form displayed in the design view. Take a look through this code  it should look very familiar from our previous example. With the addition of some standards Visual Studio .NET complies with, such as the InitializeComponent() method, the code looks very much like our initial Windows Forms application. Note the use of Application.Run in the Main method, and the fact that this Form class derives from System.Windows.Forms.Form .

The InitializeComponent() method is used by Visual Studio .NET to build the designed Form at runtime. All of the controls and properties a developer sets during design time are set at run time in this method. As changes are made to the Form in design time Visual Studio .NET will update this method.

Switch back to design view in order to add some controls to this form to make it more useful and interesting. Note that when you select the form , the Properties window extensively in setting the various properties of controls in our Windows Forms applications. It is a very important component of the Visual Studio .NET IDE, as using it is much easier than looking up the names of each property of every control in the .NET documentation. There are several buttons located at the top of this window. The first two change the way the properties are displayed. The first groups the displayed items into logical categories, such as all properties dealing with appearance, behavior, design, and so on. The second button sorts all of the properties in alphabetical order. The next two buttons toggle between displaying the properties or events. We will discuss events and how to add them to controls next. The last button opens this project's property pages:

click to expand

Set the following properties of the form by editing them directly in the Properties window.

Property

Value

Text

Data Entry Form

Size

300, 220

(Name)

frmMain

StartPosition

CenterScreen

These settings will create a 300 by 220 pixel window centered in the screen. The Name property is a very important property available on all controls. This value is used as the object name of the class's member variable, and is used to reference the control in code.

Now add two Button controls to the form . Set the properties of the two Button controls to the following:

Property

button1 Value

button2 Value

(Name)

btnSave

btnCancel

Location

125, 157

210, 157

Size

78, 25

78, 25

Text

Save

Cancel

Here we are changing the default names of the Buttons to a more standardized naming scheme, and positioning them to the location we want them on Form1 , as well as sizing them to the correct size.

Return to the code view to examine what Visual Studio .NET has done during this time. You should see the addition of two new member variables in the Form class, much like we did previously when creating the class by hand. If you expand the region titled " Windows Form Designer generated code " you will see the InitializeComponent() method where all of the controls on the form are properly initialized and configured. This method is invoked in the form's constructor.

Next add three TextBox controls and three Label controls to the Form . Assign the following properties to each:

Property

TextBox1

TextBox2

TextBox3

Label1

Label2

Label3

(Name)

txtFName

txtLName

txtSSN

label1

label2

label3

Location

97, 25

97, 61

97, 99

20, 25

20, 62

20, 99

Size

115, 20

115, 20

115, 20

70, 18

70, 18

70, 18

Text

(Blank)

(Blank)

(Blank)

First Name:

Last Name:

SSN:

You should now have a Form that looks like a standard data entry screen for user information. An end-user could use this screen to enter their first and last name as well as their Social Security Number. At this point Form1 should look like this:

click to expand

Events

Windows applications are event-driven, and without adding code that responds to events a Windows application cannot be very interactive for a user. Visual Studio .NET makes it very simple to add code to respond to the events generated by the user and the system.

The Properties window is updated to reflect the complete list of events that can be handled from this object. To see this list, simply select the fourth button from the left (with a Potter-esque lightening bolt on it). This will display a list of the events for the currently selected object, and show any events that have code associated with them. The screenshot below shows the event list when a Form object is selected:

click to expand

Adding an event can be accomplished in one of two ways. To add the default event for a control simply double-click on it in design view. For example, double-click on the btnSave button in the Designer; the appropriate event code will be included in the Form class, and you will be taken to the event handler for that button's Click event. This is because the Click event is the default event of the Button class. Note that if you look back through the code you will see similar code to what we did by hand previously, but VS .NET does all this work for you.

The other way to add event handlers to your code, and the only option if you are not adding the default event, is to utilize the Properties window. Again, the correct plumbing code will be inserted into the Form class, and you will be taken to the event handler for the selected event.

Note that the Properties window. You might do this if you have many buttons that all do similar things and require similar processing, or textboxes that all require the same validation logic.

Let's add some code in the event handlers of our two Button controls. Add the Click event handler to the two existing Buttons (you can do this by double-clicking on a Button control or utilizing the Properties window). Add the following code to the file:

 private void btnSave_Click(object sender, System.EventArgs e)       {   SaveFile();   }       private void btnCancel_Click(object sender, System.EventArgs e)       {   Clear();   }   private void SaveFile()     {     //Save the values to an XML file     //Could save to data source, Message Queue, etc.     System.Xml.XmlDocument aDOM = new System.Xml.XmlDocument();     System.Xml.XmlAttribute aAttribute;     aDOM.LoadXml("<UserData/>");         //Add the First Name attribute to XML     aAttribute = aDOM.CreateAttribute("FirstName");     aAttribute.Value = txtFName.Text;     aDOM.DocumentElement.Attributes.Append(aAttribute);         //Add the Last Name attribute to XML     aAttribute = aDOM.CreateAttribute("LastName");     aAttribute.Value = txtLName.Text;     aDOM.DocumentElement.Attributes.Append(aAttribute);         //Add the SSN attribute to XML     aAttribute = aDOM.CreateAttribute("SSN");     aAttribute.Value = txtSSN.Text;     aDOM.DocumentElement.Attributes.Append(aAttribute);     //Save file to the file system     aDOM.Save("UserData.xml");     }     private void Clear()     {     //Erase all the text     txtFName.Text = "";     txtLName.Text = "";     txtSSN.Text = "";     }   

This simple example saves the data entered by the user to an XML file on the file system. Most applications will utilize ADO.NET to save the information to a back-end data source, however, for this example we are simply going to export a small XML file.

We use private methods to perform the actual functionality so we can utilize the same functionality from menu options later on. Most of the code in the SaveFile() method involves writing out the XML file containing the user-supplied data. The Cancel button's Click event calls the Clear() method, which merely clears all the textbox controls to empty. Note that in a complete application this would probably also close this window and return the user to a main screen.

Note that as this book is going to press there is a bug in Visual Studio .NET that occasionally requires a developer to manually change the name of the Form class used in the Main() method. If you have an error when compiling navigate to the Main method and ensure it looks like the following code. Make sure the object creation code uses the frmMain class name. When a Form class' name is changed this line is not always updated.

 static void Main()  {   Application.Run(new frmMain());   } 

If you run this application at this point you will have a small data entry window that is fully functional. You can enter data, save it to an XML file, and clear all the values. This is simple, but it demonstrates how to create applications using Visual Studio .NET.

Resizing Windows

One problem with our simple data entry window is that when it is resized the controls stay locked in place. This looks funny and unprofessional, and a high quality application should support the ability to resize and position a window in any fashion the user desires. Any developer who has written code to handle resizing and replacement of controls will appreciate how easy the .NET Framework and Windows Forms make this task. With a single property almost all of this work can be handled by the .NET Framework.

The Anchor property performs this magic, and it is a member of almost all classes in the System.Windows.Forms namespace because it is a property of the System.Windows.Forms.Control class . Recall that most controls derive from this class.

The Anchor property is set to a combination of one or more of the parent's edges. Setting one of these edges in the Anchor property will cause the control to maintain the relative position between its edge and its parent's as the form is resized and moved. For example, if a Button is placed on a form and the Anchor property is set to Left, Right then the Button will always maintain the same relative distance between the right edge of the form and the left edge of the form , without any custom code required. This property is very important to designing friendly user-interfaces, and should be experimented with extensively to understand how it works.

Visual Studio .NET includes a handy pop-up window to set this property to the correct combination. This pop-up window allows a developer to graphically select the sides to anchor the control to. This pop-up window can be found as part of the Properties window.

We are going to use the Anchor property to create a more effective and visually appealing user interface for our data entry screen.

Select the three TextBox controls in the Visual Studio .NET design environment. Change the Anchor property to Top, Left, Right using the pop-up window. This will maintain the distance between the top, left, and right of the parent's edges, thereby resizing the control correctly.

Select the two Button controls and change their Anchor property to Bottom, Right . This will maintain their position close to the bottom-right of the form. Run the application and resize the window to see how the controls now dynamically adjust themselves. Note that the window can be resized and the controls will resize and reposition themselves dynamically.

Again, this property is absolutely crucial in designing professional-level user-interfaces in .NET, and using it reduces the amount of work required by developers. This frees developers to concentrate on solving real business problems instead of low-level resizing issues.

Menus

Menus are used in almost every Windows application, and they provide an excellent way to present users with the options available to them in a non-invasive manner. There are two different types of menus. The most common is a main menu, which is located at the top of a window, and often includes items like File , Edit , and Help . Some applications also contain context-sensitive menus to allow users access to specialized information about specific topics or items. Context-sensitive menus are hidden until the user presses the right mouse button  then the menu is displayed at the cursor location.

Windows Forms provides full support for adding both types of menus to an application. The System.Windows.Forms.Menu class provides the base class for all menu classes in the system. The MainMenu class represents a main menu, and can be associated with a form , which causes it to display at the top of the form . This menu contains a collection of MenuItem objects, which each represent a separate menu option.

The ContextMenu class is available for adding context-sensitive menus to an application. This class also contains a collection of MenuItem objects, but the ContextMenu can appear in any location in a form , not just at the top of a window like the MainMenu class.

We are going to add a menu to our data entry application. Adding a menu to a Windows Forms application is as easy as adding any standard control like a Button or TextBox . Select the MainMenu control from the Toolbox and draw a box on the design surface. This will add the menu to the top of the form . Simply select the menu and type File to add the first menu item. Now when you click on File a new menu will display underneath, which can be added to just as we added the File menu item. You can continue to type in MenuItems , thereby creating the actual structure of the menu system graphically in the IDE:

click to expand

Use the menu system to create the following menu. Note that by entering a single dash character a separator line is created. This is very useful for partitioning off groups of choices in a menu. Another important thing to remember is that by prefacing a character with the ampersand (&) character that character becomes the accelerator key for this menu item. Therefore a user can select the menu choice by using only the keyboard, which is important for users who may not be able to or who prefer not to use a mouse.

Top Level Menu Item

Contained Menu Items

Text  & File

Name  mnuFile

Text  & Save

Name  mnuSave

 

Text  & Cancel

Name  mnuCancel

 

Text  "-" (Single Dash)

Text  E & xit

Name  mnuExit

Text  & Color

Name  mnuColor

Text  & Gray

Name  mnuGray

RadioCheck  true

Checked  true

 

Text  G & reen

Name  mnuGreen

RadioCheck  true

 

Text  & Blue

Name  mnuBlue

RadioCheck  true

 

Text  & Red

Name  mnuRed

RadioCheck  true

 

Text  & Purple

Name  mnuPurple

RadioCheck  true

Run the application and see that you now have a window with a working menu in place. However, nothing actually happens when a menu item is selected. To change this event handlers must be added behind the individual menu items. We will continue with this same example and add event handling so that users can actually use the menu.

The individual MenuItems are each controls like any other, and they can be selected in the design surface. Doing this exposes their available properties and events in the Properties window. Use the event list to add Click event handlers for the Save , Cancel, and Exit menu choices. Add the following code in the new event handlers.

 private void mnuSave_Click(object sender, System.EventArgs e)       {   SaveFile();   }       private void mnuCancel_Click(object sender, System.EventArgs e)       {   Clear();   }       private void mnuExit_Click(object sender, System.EventArgs e)       {   Close();   } 

buttons call. This is a good design practice, because when code is updated and changed as project requirements change it is always a good idea to have to change things in one place only. Having duplicate code in the button event handlers and the menu event handlers is asking for trouble.

Dynamic Menus

Menus are often used to reflect the state of the application. As the user makes choices and changes in the application the menu must also reflect these changes. Menu items can be added, removed and modified to reflect the current application state. Again, MenuItems act like any other component and can be manipulated as such.

MenuItems can have a check placed next to them to illustrate the currently selected option. This is very useful to users as they can instantly appraise themselves of the state of the application. The Checked property is a Boolean flag that can be set to show or hide a check mark next to the menu item. Additionally, if the RadioCheck property is set to True the check will appear as a simple dot  this usually means the choices are mutually exclusive of each other. Therefore only a single menu item can be selected at a time with the RadioCheck property. It is important to remember that this is only a guideline; the .NET Framework does not enforce this rule.

We are going to add some code behind the color menu items to change the background color of the form . We will do this using a common event handler for all of the MenuItem objects.

In our application add the following method to the frmMain class.

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

We are adding an event handler manually here instead of allowing the Visual Studio .NET IDE to do it for us. We need to do this so that we can associate this single method with each of the menu items' Click event handlers. This will enable us to control the state of the menu and the application from this single method.

Back in the design view of the IDE, click on the Gray menu item. In the Properties window switch to the event view and select the Click event. Click on the drop-down arrow to display a list of method names that can be associated to this event. This is how to attach custom methods to events. Select the mnuItems_Click() method from the list. Repeat this procedure with each of the color menu items. Each item's Click event should be associated with the same method.

Now that each object is associated with the same event handler method, add the following code to update the form's BackColor and the menu state as well.

 private void mnuItems_Click(object sender, System.EventArgs e)       {   MenuItem aObj;     //Set the BackColor of the form based on the selected object     if(sender == mnuGray)     this.BackColor = System.Drawing.SystemColors.Control;     else if(sender == mnuGreen)     this.BackColor = Color.Green;     else if(sender == mnuBlue)     this.BackColor = Color.Blue;     else if(sender == mnuRed)     this.BackColor = Color.Red;     else if(sender == mnuPurple)     this.BackColor = Color.Purple;     //Set all checkboxes to false     mnuGray.Checked = false;     mnuGreen.Checked = false;     mnuBlue.Checked = false;     mnuRed.Checked = false;     mnuPurple.Checked = false;     //Change the selected item to checked     aObj = (MenuItem)sender;     aObj.Checked = true;   } 

This code uses the fact that the sender parameter to an event handler is the object that raised the event. This is required because this event handler is used by all of the MenuItem objects. Therefore, the first step is to determine the menu item selected by the user, and then change the form's BackColor to the respective color.

The next step is to set a check next to the appropriate menu item. We can do this by simply setting the sender's Checked property to true after casting it to a MenuItem object. However, before we do this we need to set all of the MenuItem objects to unchecked since we don't have anything telling us what menu item was checked previously. Remember that just by checking a menu item the previously checked item will not become unchecked  we must provide this custom logic ourselves , unfortunately .

Run the application and select different color options. You should see the background of the window change color, and the checkbox in the menu update to reflect the current color.

Context Menus

Most Windows applications allow the user to right-click and bring up a context-sensitive menu. This means the menu choices are based on the object, or context, the user has selected. Context menus allow the application to present additional information or choices to the user as they request additional information.

Context-sensitive menus can be added to Windows Forms applications very easily. We are going to add a context-sensitive menu to our data entry window that has the Save and Cancel choices available when the user right-clicks anywhere on the Form .

To add a context menu to a form simply add the ContextMenu control from the Toolbox to Form1 . Once the ContextMenu object has been added to the form it will appear in the footer area below the form design surface. When this icon is selected, the main menu, if it exists, will disappear from the form and be replaced with the context menu itself. This can now be edited in the design surface by simply typing in the different menu items, just like editing the main menu. Although it appears that the menu will be displayed at the top of the form like the main menu, it will actually be invisible and not displayed until we assign it to the form .

After adding the ContextMenu to Form1 , add the following menu items by typing in the following values:

Menu Item Name

Text Property Value

mnuSaveContext

Save

mnuCancelContext

Cancel

Again, each menu item is a separate MenuItem object and has properties that can be set in the Properties window and select the Click event. In the drop-down list select mnuSave_Click for the mnuSaveContext MenuItem and mnuCancel_Click for the mnuCancelContext MenuItem . This will wire up these events to the same event handlers that are invoked when the main menu items are clicked.

Now we have a context menu, but we have yet to actually have it appear when we right click on the Form . To add a context menu to a Form the ContextMenu property of the Form object must be set to our ContextMenu object. Once this has been set the form will automatically display the ContextMenu when it receives a right-click from the user. It will display the ContextMenu at the location the user right-clicked without any additional code being required. Note that this property is dynamic and can be updated at run-time. It is also important to note that this property is actually a member of the Control class, meaning that almost all Windows Forms controls have this property, which can be set to a ContextMenu object. This allows you to hook a right-click context-sensitive menu into almost any control.

Set the ContextMenu property of Form1 to contextMenu1 using the Properties window. A combo box will display the current ContextMenu objects available to choose from on the form . Multiple ContextMenus can be added to a form , although only one can be assigned to a form at a time.

Run the application and right-click anywhere on the form to see the context menu displaying two options, Save and Cancel :

click to expand

Note that when you right-click on one of the textboxes you will receive a very different context menu. This is because the TextBox control has a predefined context-sensitive menu, and when a custom context menu isn't set this default menu will be used. If you set the contextMenu1 object to the ContextMenu of the TextBox controls you will see our custom context menu replace the standard TextBox menu.

Dialogs

Dialogs are a special type of Form used to capture user information or interact with the user in Windows applications. There exists a set of predefined dialog boxes for capturing common information such as file locations, colors, and printer settings. A custom application will often use custom dialog boxes to facilitate the collection of data from endusers.

Creating a dialog box is very similar to creating a standard Form . In fact, exactly the same process is used to add one to a Visual Studio .NET project. The key difference is the FormBorderStyle enumeration, which must be set to FixedDialog . This makes the window non-sizeable and causes it to look like a Windows dialog box. It is also the standard Windows practice to remove the ControlBox , MinimizeBox , and MaximizeBox from a dialog box, so these properties should be set to false in the Properties window.

Any of the standard Windows Forms controls can exist on a dialog box. The design surface in Visual Studio .NET is the same used to design standard Forms , and the same options are available to developers. Again, standard Windows practice is not to include a menu on a dialog box, but this is optional.

Modal vs. Modeless

When we want to display the dialog box itself, there are two choices: modal or modeless. These terms relate to how the dialog interacts with the rest of the application. A modal dialog blocks the current thread and requires the user to respond to the dialog box before continuing with the application. A modeless dialog box is more like a standard window that allows the user to switch the focus back and forth between it and other windows.

Deciding when to use the two types of activation depends entirely on the type of application being designed. If the application must get and process a key piece of data from the user before any other processing can continue, such as login information, then a modal dialog box is the only choice. Also, many users have come to expect dialog boxes to be modal and require processing before continuing with the application, much like the standard Windows File Open and Save dialogs. Therefore providing a modeless dialog box may confuse some users who don't understand why they can switch away from it. However, as every application and project is different you must evaluate the needs of your users before deciding the best course of action. By understanding how each type of activation is used in .NET hopefully you can make the best choice.

Dialog Box Results

It is often very important to understand how the user closes a dialog box. A perfect example is a File Open dialog. If the user selected a file the next obvious task for the application is to load that file, however if the user clicked the Cancel button or closed the dialog box the application should not load any file as this would be very confusing to the user!

The key to understanding how a user has interacted with a dialog box is the DialogResult enumeration. The calling code can interpret how the user closed the dialog box by reading this value. The possible values for this enumeration are below:

Value

Description

Abort

This value is usually returned when a user selects a button labeled Abort. In this case the user would like to cancel the current operation and not save the changes made.

Cancel

This value is usually returned when a user selects a button labeled Cancel, closes the dialog box by hitting the "x" button, or presses the Esc key. The user would like to cancel the changes made and return to the state before opening the dialog box.

Ignore

This value is usually returned when a user selects a button labeled Ignore. This could be used when the application warns the user of certain error conditions, but the user selected the Ignore command.

No

This value is usually returned when a user selects a button labeled No. This is common when the dialog box is used to ask the user a yes/no question, such as "Do you want to save the file?"

Yes

This value is usually returned when a user selects a button labeled Yes. This is the counterpoint to the No return result, and used in the same situations.

None

Nothing is returned from the dialog box. This means that the dialog box is still running.

OK

This value is usually returned when a user selects a button labeled OK. This is common for informational dialog boxes or warning messages where it is simply important for the user to acknowledge the receipt of the information.

Retry

This value is usually returned when a user selects a button labeled Retry. This is useful when an operation failed that might succeed if retried, such as a database connection or removable disk detection.

To gain access to this value you must use the Form's DialogResult property. This property is public, and can be accessed even after the user has closed a dialog box. When a user closes a dialog box the form is hidden from the user, not unloaded. This allows code to continue access any member variables and data the user may have set on the dialog box, and allows the developer to access the returned DialogResult value. In addition, if the application needs to display the dialog box again it is very quick to simply unhide the dialog instead of reloading it. As a result of this behavior, however, to actually destroy the dialog the Form.Dispose() method must be called when your code no longer requires the dialog resource.

Opening a Dialog

There are two ways to open a dialog box, one for modal display and one for modeless display. To show a dialog as a modal dialog, the following method is used:

   DialogResult Form.ShowDialog()   

This method is part of the Form class. The method itself accepts either no parameters or a single Form object as a parameter. This Form object represents the dialog box's owner , which is useful because all Form objects have a pointer back to their parent, allowing a dialog box to get or set data in its parent if passed the correct object in this parameter. If no parameter is passed the current window defaults to the parent.

Note that this method returns a DialogResult enumeration value. This method blocks execution, and no code after it will execute until the user closes the dialog box. When this occurs the DialogResult return code is returned and the application can continue processing. Code such as the following is very common when using modal dialog boxes:

   if (aDialogObject.ShowDialog() == DialogResult.Yes)     {     //User selected Yes     //Use the properties of the aDialogObject to perform actions     }     else     {     //User selected No do not perform action     }   

This displays the dialog box, which might ask the user if they want to save the current file. If the user selects Yes the code drops into the if block, and if they select No the else block is executed.

Displaying a modeless dialog box is much like displaying any Form , and the standard Show() method is used for this purpose. This method does not return a DialogResult value, nor does it block. The new dialog is simply displayed and the code continues to execute. Both the original window and the new dialog box are open and can be interacted with by the user. The Form.DialogResult property is the only way to retrieve the user's response. Often this form of dialog box will perform whatever action is required itself before closing.

Common Dialogs

Since Windows 95, developers have been able to utilize a set of dialogs for common functions, such as opening files, selecting fonts, and print previewing. Prior to Windows 95 each of these tasks was defined in custom dialog boxes for every application, thus creating a fragmented experience for the enduser. By standardizing these dialog resources and subsuming them into the operating system, any Windows application can take advantage of the functionality provided. These are the same dialogs all Windows users have used when opening a file or selecting a color, so people are automatically familiar with their use. This allows them to easily engage with the application and more quickly learn how to operate it.

The .NET Framework provides access to these underlying common dialogs through the following classes. Each of these classes represents a common dialog, and can be displayed as a dialog box. All of these classes exist in the System.Windows.Forms namespace:

Class

Description

ColorDialog

This allows a user to select a color from the available color palette as well as define and utilize custom defined colors

FontDialog

This dialog box displays all currently available fonts installed on the system and allows the user to choose one to use in the application

OpenFileDialog

Allows a user to open a file using the standard open file dialog box

SaveFileDialog

This allows a user to select a file, directory or network location to save the application's data to

PageSetupDialog

This dialog box allows the user to set page size, margins, and other printing features

PrintDialog

This dialog box allows the user to set common page formatting and printing features via the standard print properties dialog box

PrintPreviewDialog

This displays a document as it will appear on the currently-selected printer with the current page settings

All of these classes inherit from the System.Windows.Forms.CommonDialog class, except for the PrintPreviewDialog class. The System.Windows.Forms.CommonDialog class provides the basic functionality required to show a common dialog box. Each of the common dialog classes is displayed using the ShowDialog() method, but they each contain custom properties used to configure and query their custom functionality. We will examine each common dialog's main feature set and try to understand how each can be used in a business application.

ColorDialog

This dialog displays the common color picker dialog box. This is useful when a user is allowed to customize the background of a Form or control, and you want to provide them with an intuitive way to select their preferred color.

The main property that will be used with this class is the Color property. This contains the selected color when the dialog box returns control to the application. This property is a Color structure, which is useful because the .NET Framework expects this structure in other methods and properties that work with colors.

Another key feature of this dialog box is the ability for users to define and use a set of custom-defined colors. This feature is enabled by default, but can be disabled by setting the AllowFullOpen property to false .

Like all dialog boxes, the return value from ShowDialog() must be examined to understand how the user exited the dialog. Here is a snippet of code using this class:

   ColorDialog aClrDialog = new ColorDialog();     aClrDialog.AllowFullOpen = false;     aClrDialog.Color = this.BackColor;     if (aClrDialog.ShowDialog() == DialogResult.OK)     {     this.BackColor = aClrDialog.Color;     }     aClrDialog.Dispose();   

This example creates a new ColorDialog object and sets the AllowFullOpen property to false . This disables the button that allows users to select custom colors. Note that then the Color property of the ColorDialog object is set to the current color of the frmMain Form object. This initializes the current color in the dialog and allows the user to see the existing color. The object that will be updated by the color selected in the dialog should initialize the dialog before displaying it to the user. Finally, the ShowDialog() method is called and the return value of that is checked. If the user selects OK then the code can now use the Color property of the ColorDialog class to access the color the user selected and update the frmMain BackColor property.

Note that you can use any common dialog box like this example, or you can add the component to a Form in Visual Studio .NET. The code will be very similar, but the dialog class will be available to all methods in the Form , and all of these properties can be set at design time in the Properties window. If you want to respond to an event raised by a common dialog box you must add the class to the Form in design view, as this allows you to write event handlers within the Form .

FontDialog

This class allows a user to select a font style, size, and color. This is very useful in applications that perform text processing, but again it could also be used to allow the user to customize their user experience by selecting the font type to display in the data input and reporting screens.

This class contains a large number of properties that can be set to configure the dialog box's functionality in exactly the right manner:

Property

Description

Color

This property gets or sets the currently selected font color. Note that the ShowColor property must be true for this to be a valid.

Font

This is the most important property of this dialog. It returns the Font structure representing the font selected by the user. This can then be applied to a Control or Form object to change the font.

MaxSize

Gets or sets the maximum point size a user can select in the dialog.

MinSize

Gets or sets the minimum point size a user can select in the dialog.

ShowApply

Boolean property that can be set to true to display an Apply button. If this is used, an event handler must be written to capture the Apply event when it occurs. This is because when the user selects Apply, the control still does not return to the application, but the event handler can then process the changed font in the application.

ShowColor

Boolean property that can be set to true to display a list of colors. This allows the user to select the color for the font as well as the style and size.

ShowEffects

Boolean property that can be set to true to allow the user to specify strikethrough , underline, and text color options.

An event handler can be written to respond to the Apply event when it is raised by the user pressing the Apply button. This event can be subscribed to and implemented exactly like the events we have seen before.

   FontDialog aFontDialog = new FontDialog();     aFontDialog.ShowColor = true;     aFontDialog.ShowEffects = true;     aFontDialog.MinSize = 1;     aFontDialog.MaxSize = 35;     aFontDialog.Font = SomeControl.Font;     if (aFontDialog.ShowDialog() == DialogResult.OK)     {     SomeControl.Font = aFontDialog.Font;     }     aFontDialog.Dispose ();   

Note that we first initialize the FontDialog object with several settings, including the Font property of SomeControl , which represents the control object we are updating. It is good practice to initialize the Font property to the font of the control you will be replacing so the user sees the currently selected font in the dialog box, and is not confused . If the user selects OK in the FontDialog , then the Font property of SomeControl is updated to reflect the user-selected font.

OpenFileDialog

This class is very useful, as many applications require the user to navigate the file system in order to open and make use of data files. This dialog is the standard Windows dialog for opening files, and users should be very familiar with it.

This class contains a number of properties used to set the appearance and behavior of the dialog box itself. Both this class and the SaveFileDialog class inherit from the base class FileDialog that provides much of the basic functionality of working with files. For this reason many of the properties are shared. The key properties are in the following table.

Property

Description

CheckFileExists

Set this to true to cause the dialog box to display a warning if the user specifies a file name that does not exist. This way your code does not have to check for a valid path name. The default is true .

FileName

This important property is used to set and retrieve the file name selected in the file dialog box.

FileNames

If Multiselect is enabled, this property will return an array of file names the user selected.

Filter

This sets the file name filter string, which determines the choices that appear in the "Files of type" box in the dialog box.

FilterIndex

The index of the filter currently selected in the file dialog box.

InitialDirectory

The initial directory displayed by the file dialog box.

Multiselect

Boolean property that is set to indicate whether the dialog box allows multiple files to be selected. The default is false .

ReadOnlyChecked

Boolean property indicating if the read-only checkbox is selected. Default is false . This is used to indicate that the user wants to open the file as read-only.

RestoreDirectory

Boolean property indicating whether the dialog box restores the current directory before closing. The default is false .

ShowHelp

Boolean property indicating whether the Help button is displayed in the file dialog.

ShowReadOnly

Boolean property indicating whether the dialog contains a read-only check box. The default is false .

Title

The file dialog box title to display.

This class is used to get a file name or multiple file names from the user. Once this has been done the application can process the file or files indicated by the user. The Filter property is key to providing a useful interface to the user. By narrowing the displayed files to only those relevant to the current application the user is more likely to find the correct file.

The Filter property is a string that can contain multiple filtering options. Each filter contains a brief description, followed by a vertical bar ( ) and the filter pattern as a DOS search string. The strings for different filtering options are also separated by a vertical bar. Therefore, a dialog that has two options: text files ( *.txt ) and all files ( *.* ) would utilize a filter string like this: " Text files (*.txt)*.txtAll files (*.*)*.* "

You can add multiple filter patterns to a single filter by separating the file types with semicolons. For example: " Image Files(*.BMP;*.JPG;*.GIF)*.BMP;*.JPG;*.GIFAll files (*.*)*.* "

The following code creates an OpenFileDialog object, configures some properties on it, and displays it to the user to allow them to select a file. The application can then use the FileName or FileNames property to process the referenced file or files. Note that these are mutually exclusive; if Multiselect has been set to true the code will use the FileNames property, otherwise it will use the FileName property.

   OpenFileDialog aOpenFileDialog = new OpenFileDialog();     aOpenFileDialog.Filter = "Text Files (*.txt)*.txtWord Documents" +     "(*.doc)*.docAll Files (*.*)*.*";     aOpenFileDialog.ShowReadOnly = true;     aOpenFileDialog.Multiselect = true;     aOpenFileDialog.Title = "Open files for custom application";     if (aOpenFileDialog.ShowDialog() == DialogResult.OK)     {     //Do something useful with aOpenFileDialog.FileName     //or aOpenFileDialog.FileNames     }     aOpenFileDialog.Dispose();   

This dialog box has three options in the " Files of Type " combo box because of the Filter string. One option is Text Files , another Word Documents , and a third is All Files . It is a good practice to allow users to select an All Files option, as most Windows applications have this convention.

SaveFileDialog

This dialog box is very similar to the OpenFileDialog , and in fact they both derive from a common base class. The basic function of this dialog box is to allow a user to select a location to save data. Unlike the previous dialog box, this can be an entirely new file.

Many of the properties are the same as the OpenFileDialog class; however, the following properties are explicitly members of OpenFileDialog and do not exist in the SaveFileDialog class:

  • CheckFileExists

  • Multiselect

  • ReadOnlyChecked

  • ShowReadOnly

However, the following two properties are only valid as members of the SaveFileDialog class:

  • CreatePrompt   boolean property that can be set to true to provide a prompt for the user if they specify a file name that does not exist. This reminds the user that a new file will be created, and can be a good check to ensure they did not simply misspell a file name. The default value is false .

  • OverwritePrompt   boolean property than can be set to true to provide a warning prompt if the user selects a file name that already exists. This is a common warning to prevent a user to overwriting an important file accidentally . The default is true .

Note that the other properties operate in exactly the same manner, including the Filter , Title , and FileName properties. The following code shows how to use this class:

   SaveFileDialog aSaveFileDialog = new SaveFileDialog();     aSaveFileDialog.Filter = "Text Files (*.txt)*.txtWord Documents" +     "(*.doc)*.docAll Files (*.*)*.*";     aSaveFileDialog.CreatePrompt = true;     aSaveFileDialog.OverwritePrompt = true;     aSaveFileDialog.Title = "Save file for custom application";     if (aSaveFileDialog.ShowDialog() == DialogResult.OK)     {     //Do something useful with aSaveFileDialog.FileName;     }     aSaveFileDialog.Dispose();   
PageSetupDialog

This dialog box is used to set page orientation and margins.

The key property in this class is Document . This is required before the ShowDialog() method can be invoked, and an exception is raised if it has not been assigned a value. The Document property accepts a PrintDocument object, which is a member of the System.Drawing.Printing namespace. This object is crucial to the printing process in .NET, and represents the pages that an application will print out to. By setting the properties of this object and using GDI+ calls to draw to its surface, the application can print to the printer.

We are not going to cover printing in this chapter (for more on this you should refer to Chapter 19), although several of the common dialogs deal with configuring the printer and page settings. The examples in the following sections are not fully fleshed out, but demonstrate how the dialog boxes are invoked and utilized:

   PageSetupDialog aPageSetup = new PageSetupDialog();     System.Drawing.Printing.PrintDocument aDoc = new     System.Drawing.Printing.PrintDocument();     aPageSetup.Document = aDoc;     if (aPageSetup.ShowDialog() == DialogResult.OK)     {     //Do something useful with aPageSetup.Document;     }     aPageSetup.Dispose();   

This code creates a new PageSetupDialog object, associates a new PrintDocument object with it and displays the dialog.

PrintDialog

This dialog is used to select the printer, number of copies, and pages to print in a document. Like the previous dialog this object requires a valid PrintDocument object to be associated with the Document property before it can be displayed.

The object also contains the following key properties:

Property

Description

AllowPrintToFile

Boolean property that can be set to true to display the "Print to file" checkbox in the dialog. This is true by default.

AllowSelection

Boolean property that can be set to true to allow for printing only the current selection. This is false by default.

AllowSomePages

Boolean property that can be set to true to indicate the From Page and To Page option is enabled. This is false by default.

Document

The PrintDocument property representing the current printing surface.

PrintToFile

Boolean property that can be set to true to indicate the "Print to file" check box is checked. When the dialog returns this can be checked to see if the user wishes the application to print the document to a file. The default is false .

ShowHelp

Boolean property that can be set to true to indicate the Help button should be displayed. The default is false .

Like the other common dialog classes, these properties are configured before calling ShowDialog() in order to display the correct dialog box for the user. Once the ShowDialog() call has returned these can be queried to understand what the user wants. The code for using this class is very similar to the previous code:

   PrintDialog aPrintDialog = new PrintDialog();     System.Drawing.Printing.PrintDocument aDoc = new     System.Drawing.Printing.PrintDocument();     aPrintDialog.Document = aDoc;     aPrintDialog.AllowSomePages = true;     aPrintDialog.AllowSelection = true;     if (aPrintDialog.ShowDialog() == DialogResult.OK)     {     //Do something useful with aPrintDialog.Document;     }     aPrintDialog.Dispose();   
PrintPreviewDialog

This class provides a very quick way to introduce print previewing capabilities into an application without much custom code. This class accepts a PrintDocument object in its Document property, and the same code that handles printing to a printer will render the document to this dialog box.

This dialog box supports scaling, zooming, pagination, and a host of other options. The many properties are too numerous to list here, but the basic principle is much like the other classes we have examined.

Visual Inheritance

In previous chapters (and Appendix A) we have seen that the object-oriented nature of C# facilitates inheritance, which encourages code reuse, and thus allows for less code to be written. Recall that inheritance allows a class to utilize all of the properties and methods of its parent class (or interfaces) and gives it the ability to extend these methods and properties as desired.

The .NET Framework takes the concept of inheritance and allows a developer to use it to develop Windows Forms applications. A Form object can inherit from another Form object, thus gaining access to all the contained Button s, TextBoxes , and Menus , as well as non-display- related methods and properties. This is a very powerful feature in .NET that when used properly dramatically reduces the amount of code required for creating similar screens and windows. This concept is called visual inheritance .

Recall that a Form always inherits from System.Windows.Forms . This means it gains access to all of the data members and methods of the base Form class. Implementing visual inheritance simply requires a developer to instead derive the Form object from a custom Form class instead of System.Windows.Forms . This causes all of the controls and properties in the custom Form class to pass over into the newly created Form class.

However, there are some important things to bear in mind. The access level of the various controls must be understood , just like the access level of standard inheritance. Recall that a private data member is inaccessible to any object outside of the original object, including derived objects. Therefore, unless a control is marked as protected or public the derived class cannot reference the control in any code, nor can it override any of the control's behavior. An important consideration is that private controls are still displayed in derived classes.

Using visual inheritance can be very useful when one must create a large number of screens that must have a similar design and/or do very similar functions. One common example is a data entry screen, much like the one we created earlier. If our application did not just need to input personnel records, but also automobile information, using visual inheritance to define a common style might be a good choice. Obviously we would want a similar looking screen, but some of the controls would change. Let's modify our previous example to utilize this technique.

Create a new C# Windows Application in Visual Studio .NET and name it VisualInheritance . This project can be found in the code download in the VisualInheritance folder .

Change the following properties of the default Form1 object. We are going to create a simple menu window that will provide the user with the ability to enter either personnel records or automobile records.

  • FormBorderStyle   FixedDialog

  • MaximizeBox   False

  • MinimizeBox   False

  • Size   200, 200

  • StartPosition   CenterScreen

  • Text   Main Menu

Place two Button controls on the Form . Position them in the center of the window, label them Person and Automobile , and name them btnPerson and btnAuto respectively. We will add event handlers to these later to open up each derived Form .

We are now going to add our base Form class. This Form will never be displayed directly, but we will use its visual style in all of the derived forms.

Add a new Form to the application by selecting Project Add Windows Form . Leave the default name and select OK in the Visual Studio .NET dialog box. Modify the following properties of the Form to generate a unique visual style.

  • Name   frmBase

  • BackColor   White

  • FormBorderStyle   FixedDialog

  • MaximizeBox   False

  • MinimizeBox   False

  • Size   250, 250

  • StartPosition   CenterScreen

  • Text   Base Form

This creates a white dialog box. Now add two Buttons to the lower right corner of the form . These will act as our Save and Cancel buttons. By adding them to the base class they will be visible on all derived forms , thus ensuring a common user-interface. Position the two Buttons in the lower right corner and set the following properties:

Button

Name

Anchor

Location

Modifiers

Size

Text

Button1

btnSave

Bottom, Right

159, 151

Protected

75, 23

Save

Button2

btnCancel

Bottom, Right

159, 185

Protected

75, 23

Cancel

The most important property to note is the Modifiers property. This sets the isolation level of the Button class within the form . This can be set to any valid C# isolation level: public , protected , private , or internal . After modifying the property in the Properties window examine the code to see that the declarations for the two Button objects have been modified to the protected isolation level. This will be very important in allowing derived Form objects access to the Buttons .

Recall that protected members can only be accessed by derived classes; they are inaccessible to any external code. Derived Forms cannot access controls declared with the default private isolation level. Interestingly the buttons will still be displayed on derived Forms , but no event handlers can be added, as the objects cannot be accessed from the derived class. Any event handlers placed within the parent Form would fire, so one could write code that remains in the parent Form but executes when a derived Form's buttons are clicked. This can be a very powerful design technique, but for our example we will write event handlers in each of the derived Forms . We are using visual inheritance to enforce a standard user-interface look and feel.

Finally we are ready to add a derived Form . However, Visual Studio .NET requires that base Form classes are compiled first, so we must first build the project at least once. Once this has completed, select Project Add Inherited Form . Leave the default name of the class file by clicking Open in the resulting dialog box. Next select the correct base Form class to use. A dialog box displays the currently available Forms in the project, and allows you to derive the new Form class from any of them. Select the frmBase class and click OK .

A new Form will be generated, but it will look exactly like the original frmBase class. It has the same white BackColor and the two b uttons : Save and Cancel . Change the Text property of the Form to Personnel Information, and add four Label controls and four TextBox controls. Change the Text property of the Labels to " First Name: , " " Last Name:" , " DOB: " and " SSN: " Change the Name property of the TextBox controls to txtFName , txtLName , txtDOB , and txtSSN , and blank out their Text properties. The new Form should look something like the following screen:

click to expand

Repeat the process of adding a new Inherited Form to the project. Again derive it from the frmBase class. This time change the Text property of the Form to Automobile Information and the Name property to frmAuto and add four Labels to the Form titled: " Manufacturer: ", " Model: ", " Year: ", and " Color: " Again add four TextBox controls positioned next to the Labels , and change the Name property to txtManufact , txtModel , txtYear , txtColor for each respective TextBox .

We are now ready to add event handlers to our derived Forms . Remember that in a functional application the Save button would likely utilize ADO.NET or a back-end business object to save the data to a data store. In this sample the data is simply persisted to a small XML file.

Add the following Click event handlers for the Personnel Information Form for both the Save and Cancel buttons. Even though these buttons have been inherited from a base class, they can still be manipulated and events added like any other controls. This is because the base class defines these objects' isolation level as protected  if they were private the derived classes would not be able to access the Button objects at all.

 private void btnSave_Click(object sender, System.EventArgs e)       {   //Save the values to an XML file     //Could save to data source, Message Queue, etc.     System.Xml.XmlDocument aDOM = new System.Xml.XmlDocument();     System.Xml.XmlAttribute aAttribute;     aDOM.LoadXml("<PersonnelData/>");         //Add the First Name attribute to XML     aAttribute = aDOM.CreateAttribute("FirstName");     aAttribute.Value = txtFName.Text;     aDOM.DocumentElement.Attributes.Append(aAttribute);     //Add the Last Name attribute to XML     aAttribute = aDOM.CreateAttribute("LastName");     aAttribute.Value = txtLName.Text;     aDOM.DocumentElement.Attributes.Append(aAttribute);     //Add the DOB attribute to XML     aAttribute = aDOM.CreateAttribute("DOB");     aAttribute.Value = txtDOB.Text;     aDOM.DocumentElement.Attributes.Append(aAttribute);     //Add the SSN attribute to XML     aAttribute = aDOM.CreateAttribute("SSN");     aAttribute.Value = txtSSN.Text;     aDOM.DocumentElement.Attributes.Append(aAttribute);     //Save file to the file system     aDOM.Save("PersonnelData.xml");   }       private void btnCancel_Click(object sender, System.EventArgs e)       {   txtLName.Text = "";     txtFName.Text = "";     txtDOB.Text = "";     txtSSN.Text = "";   } 

This code should look familiar to you, because it is very similar to the XML persistence code from the previous example. The basic idea is to serialize the contents of the TextBoxes into an XML file and save it to the file system. The Cancel button clears the TextBox controls.

The code for the Automobile Information Form is extremely similar except for the different TextBox names being used and a different XML file being generated. As it does not demonstrate anything new we will not show it, although the complete code for this project can be found in the code download in the VisualInheritance directory.

Finally, add the following event handlers for the Form1 Button's Click events. These open the appropriate derived Form .

 private void btnPerson_Click (object sender, System.EventArgs e) {   Form3 aForm = new Form3();     aForm.ShowDialog();   } private void btnAuto_Click(object sender, System.EventArgs e) {   Form4 aForm = new Form4();     aForm.ShowDialog();   } 

The application is again fairly simple, with a small dialog box providing a basic menu for the user to enter either personnel records or automobile information. The user can select either choice and a window will be displayed that displays a common visual theme and interface. Obviously a more complex standardized userinterface can be developed than white backgrounds and two b uttons , but this is a start.

Imagine an entire Form class library, with a base Form object providing the overall look and feel of the derived windows, with successive layers of classes defining specialized versions of Forms . Base classes could exist for data entry screens, About boxes, or any other required window in an application. This could be extended to the entire enterprise, enabling a common look and feel for the entire range of applications built by your organization. Ultimately this will lead to more positive user acceptance and greater user productivity, as users become accustomed to the organization's common application user interface.

  


Professional C#. 2nd Edition
Performance Consulting: A Practical Guide for HR and Learning Professionals
ISBN: 1576754359
EAN: 2147483647
Year: 2002
Pages: 244

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