.NET for Visual FoxPro Developers

Chapter 9
Building .NET Windows Forms Applications

Microsoft has placed a tremendous emphasis on .NET being a platform to create, consume, and deploy XML Web Services. Unfortunately, it caused many developers among the uninitiated to believe that .NET is only for building applications that access the Internet. I’ve heard the question over and over again: “Why should I use .NET if I’m not creating Web applications”? This chapter aims to dispel this notion.

You can use .NET to create standard, Windows desktop applications (known in .NET as Windows Form or WinForm applications). Truth be told, you can quite happily create C# and Visual Basic .NET applications that never think twice about the Internet. However, should you decide to provide Web access to your desktop applications, all the tools are ready and waiting for you.

This chapter shows you how to take advantage of Visual Studio .NET to create Windows Forms Applications. You’ll be amazed at how easy it is to do things you could never do in Visual FoxPro (or things you had to kludge). Most importantly, you’ll see how to use business objects in a Windows Forms application.

Creating a Windows Forms application

In this chapter you will learn about Windows Forms applications by building a simple Windows Forms application and taking a look behind the scenes each step of the way. Chapter 2, “Visual Studio .NET” showed the very basics of creating a Windows application. This chapter takes you beyond the basics and explores the process in greater detail.

A Windows Forms application typically contains a main application form, a main menu, toolbars, a variety of forms, code files containing business object classes, and references to external .NET assemblies. In this walk-through you’ll see how each element is used in a .NET Windows Forms application.

To create a new Windows Forms application:

1.       From the Visual Studio .NET Start Page, click the New Project button.

2.       In the New Project dialog look in the Project Types pane on the left and select Visual C# to create a C# Windows application or Visual Basic .NET to create a VB .NET Windows application. Then in the Templates pane on the right, select Windows Application.

3.       In the Name text box, enter the name you want to give your new Windows application. In this example I’ll enter “Simple Windows Application”.

4.       Make sure the Location box contains the directory you want to create your new application in and click OK.

At this point, VS .NET creates a new application for you, using the Windows Application template you selected. After a few moments, a new form named Form1 is displayed in design mode in the Visual Studio .NET IDE (Figure 1).

Figure 1. Visual Studio .NET creates a Windows application from the template you select in the New Project dialog.

You may be wondering what this form is for—is it supposed to be a login form or maybe just one of the forms in the application? By default, this form is meant to be your main application desktop, more or less equivalent to the main Visual FoxPro window. Take a
look behind the scenes by right-clicking on Form1 and selecting View Code from the
shortcut menu.

Namespaces in C#

Because namespaces are handled a bit differently in C# versus Visual Basic .NET, I’ll talk about namespaces for each language separately. If you’re using C#, notice the namespace declaration at the top of the code file:

namespace Simple_Windows_Application

This is the namespace automatically created by Visual Studio .NET when you created the new project—it’s basically the project name with spaces replaced by underscores. Before going any further, change the namespace to something more meaningful. Microsoft recommends that you use the “Company.Product” naming convention for your namespaces. For example, the following namespace specifies the company HW (Hentzenwerke), the product NetBook (.NET for VFP Developers Book), and I’ve added an extra “.Samples” section to indicate these are the samples for the book:

namespace HW.NetBook.Samples

Now you need to change the project’s default namespace to the same namespace. The default namespace specifies the namespace that is used for all new code files added to the project. To change the default namespace, right-click on your project in the Solution Explorer and select Properties from the shortcut menu. In the Property Pages dialog, expand the Common folder in the left pane and select General. Change the Default Namespace (Figure 2) to the new namespace, “HW.NetBook.Samples”.

Figure 2. You change your project’s default, or root namespace, in the project’s Property Pages dialog.

Namespaces in Visual Basic .NET

If you’re using Visual Basic .NET, there is no namespace declaration in the Form1.vb code file—that’s because VB .NET handles namespaces differently than C#. In VB .NET there is the concept of a root namespace for each project, which is different than C#’s default namespace. This root namespace acts as a prefix for any namespace you declare within a code file. For example, if the project’s root namespace is “HW.NetBook.Samples”, and you specify an additional “Business” namespace, the full namespace is “HW.NetBook.Samples.Business”.

To set the VB .NET project’s root namespace, right-click on the project and select Properties from the shortcut menu to launch the Property Pages dialog (Figure 3). In the Root namespace text box, enter “HW.NetBook.Samples”, and click OK to save changes.

Figure 3. You change a Visual Basic .NET project’s root namespace in the Property Pages dialog.

Behind the scenes with C#

If you created a C# Windows Forms application, scroll down to the bottom of the code file and you will see the following method declaration:

static void Main()

{

  Application.Run(new Form1());

}

As mentioned in chapter 3, “Introduction to C#”, all C# programs must have a static method named Main, which is the method called when the program is first executed. By default, the Main method is added to the Form1 class by Visual Studio .NET. If you don’t want a form to be the first object instantiated in your application, you can remove the Main method from the form, create a new class, and add the method to it.

If you’re using C#, the compiler automatically finds the class with the Main method at compile time. If you’re using Visual Basic .NET, you must specify in your project’s Property Pages dialog which object in your application is the startup object.

The ability to change your startup object is useful when you are creating a more robust application with an application object that you want to instantiate first. After doing some application startup processing, you can instantiate your main application form. For now, I’ll leave this method in the Form1 class.

Within the Main method, there is the following code:

Application.Run(new Form1());

The Application object belongs to the System.Windows.Forms namespace. Its static Run method runs an application message loop that processes messages from the operating system to your application. In this line of code, a new instance of the Form1 class is passed as a parameter to the Run method. This causes the message processing to begin and the main application form to be made visible. If you leave out this line of code the application will start up and immediately exit—similar to Visual FoxPro’s READ EVENTS.

Behind the scenes with Visual Basic .NET

If you created a Visual Basic .NET Windows application, you will not see a Main method declaration in the form’s code as you do in C#. What’s going on here?

In a VB .NET Windows application, Form1 is specified as the startup object by default; however, a form-level Main method is not exposed in the source code. Behind the scenes, the Visual Basic .NET compiler inserts a Main method into the IL code to satisfy .NET’s requirement that all programs must have a Main method. This model was chosen to help simplify VB .NET Windows Forms applications.

If you don’t want a form to be the first object instantiated in your VB .NET application, you can create a new class and add a Main method to it. For example:

Public Class MainClass

    Shared Sub Main()

        Application.Run(New Form1())

    End Sub

End Class

Next, you need to change the startup object from Form1 to your Main method. Right-click on your project and select Properties from the shortcut menu to launch the Property Pages dialog (Figure 3). In the left pane, expand the Common Properties folder and select General. In the Startup object combo box, change the selection from “Form1” to “Sub Main”.

Modifying the main application form

To learn more about .NET forms, click Form1 in design mode and bring up the Properties Window. Follow these steps to change some key properties of the form:

1.       Change the form’s Text property from “Form1” to “My Simple WinForm Application”. When you press Enter or move off the Text property, the caption of the form changes to the new text.

2.       Scroll to the top of the Properties Window and find the (Name) property to specify the name of the form class. Change the (Name) property to MainAppForm.

3.       Go to the Size property in the Properties Window and change the size to 640, 480. Note that if you expand the Size property by clicking on the plus sign (+) you can change just the Height or just the Width of the form.

4.       Select the IsMdiContainer property and set its value to “True”. “MDI” stands for Multiple Document Interface. An MDI form can contain child forms. If you don’t set this property to “True”, any child forms you launch will exist outside of the main application form. Note when you change this property to “True”, the back color of the form changes to the “Application Background” color in your Windows color scheme (on my computer, it’s dark grey).

5.       Now change the WindowState property. The default value is “Normal”, which means when you run the application the form is the same size you see in design mode. Change the value of this property to “Maximized”, so the window fills the computer screen at run time.

Before compiling and running the application, you need to make one more change to the Form1 code file. When you changed the name of the form class from “Form1” to “MainAppForm”, it changed the name of the class in the code file. However it did not change the name of the form class being passed to Application.Run(). Go back to the Main method and change the call to the Application class’s Run method as follows.

In C#:

Application.Run(new MainAppForm());

In Visual Basic .NET, rather than adding this line of code, right-click on the project and select Properties from the shortcut menu.  In the Property Pages dialog’s Startup object combo box, select “MainAppForm”, and then click OK to close the dialog.

Next, for the sake of consistency, go to the Solution Explorer, right-click on the Form1 source code file, select Rename from the shortcut menu, and change the name from “Form1.cs” to “MainAppForm.cs” (in C#) or from “Form1.vb” to “MainAppForm.vb” (in Visual Basic .NET).

Now you’re ready to compile the application. To do so, select Build | Build Solution from the main menu. To run the application, select Debug | Start from the menu or press the F5 shortcut key. Your main application form should fill the entire screen. To close the application, click the close button on the upper right side of the form.

Creating a main application menu

At this point, you need to create a main application menu where you launch other application forms you build later on. In Visual Studio .NET, you add a menu to any form in your application. To add a menu to your main application form, select the form in design mode, and then drag and drop a MainMenu class from the VS .NET Toolbox onto the form. If the Toolbox is not visible, select View | Toolbox from the main menu.

When you drop the menu on the form, rather than showing up on the form’s design surface, it is displayed in the component tray beneath the form (Figure 4).

 

Figure 4. When you add a menu to a form, it is displayed in the component tray beneath the form rather than on the form’s design surface.

If you’ve ever struggled with Visual FoxPro’s menu designer, you’re going to like the menu designer in VS .NET. To kick the tires a bit, add a few menu pads and menu bars using the menu designer.

To create a File menu pad, click in the rectangle labeled Type Here at the top of the form, and enter the text “&File” as shown in Figure 5. The “&” indicates the “F” is the menu item’s access key. At run time, pressing ALT+F selects this menu pad. When you add a new menu item Visual Studio .NET automatically gives it a generic name such as “menuItem1”. It’s best if you change this name to something more meaningful, such as “FilePad”. To do this, select the menu item, bring up the Properties Window, and set the value of the (Name) property to “FilePad”.

Figure 5. You indicate the access key of a menu item by placing an ampersand in front of it.

Next, add an Exit menu bar to the File menu pad. To do this, click in the rectangle labeled Type Here below the File pad and enter the text “E&xit”. Next, change the name of the new menu bar to “FileExitBar” in the Properties Window.

Add a second menu pad called Customers by clicking in the Type Here box to the right of the File menu pad, entering the text “&Customers”, and changing the name of the menu pad to “CustomersPad”. Add a menu bar to this pad by clicking in the Type Here box beneath the pad, entering the text “&Orders”, and changing the name of the menu bar to “CustomerOrdersBar”. This bar will be used to launch a Customer Orders form that you will create later in this chapter.

Most applications have a Window menu pad containing a list of all forms open on the desktop. In Visual FoxPro, you had to write code adding a menu bar to the Window menu pad whenever a new form was instantiated. In Visual Studio .NET, all you have to do is add a menu bar with the text “Window” and set a single property in the Property Window. To create the Windows menu pad, click in the Type Here box to the right of the Customers menu pad and enter the text “&Window”. In the Properties Window, change the name of the menu pad to “WindowsPad”. Next, change the value of the MdiList property from False to True. If this property is set to True, when an MDI child form is instantiated at run time, a corresponding menu bar is automatically added to this menu pad.

At this point, your menu system looks good, but doesn’t do much (other than the Windows menu pad). Now add some code to the Exit menu bar that closes the application when the user selects it at run time. To do this, double-click on the Exit menu bar and the following code is automatically added to your form.

In C#:

private void FileExitBar_Click(object sender, System.EventArgs e)

{

 

}

And in Visual Basic .NET:

Private Sub FileExitBar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles FileExitBar.Click

 

End Sub

This code is a form-level method acting as an event handler for the FileExitBar’s Click() event. Any code you place in this method is automatically executed at run time when a user selects File | Exit from the menu.

Text Box: ¥

When you double-click on any user interface control, VS .NET automatically adds event handler code for the control’s default event. The default event for a menu bar is Click().

What mechanism automatically calls this event handler when the Click event of the menu bar is executed? When you double-clicked the FileExitBar in the menu designer, it actually adds another line of code to your form that is hidden in the code editor. To see this code, scroll up in the code editor until you see a rectangle containing the text “Windows Form Designer generated code” (Figure 6), and expand this node.

Figure 6. The Visual Studio .NET Form and Menu Designers automatically add “behind-the-scenes” code to your form

As you can see, the Form and Menu Designer have been adding plenty of “behind-the-scenes” code to your form as you’ve been creating new menu items and setting form properties. The C# editor hides less of this code than the Visual Basic .NET editor which hides just about everything. Take a look at some of this form-level code to see what’s going on.

First, look at the form’s constructor code. This code is not hidden in C#, but is hidden in Visual Basic .NET.

Here’s the C# form constructor code:

public MainAppForm()

{

  //

  // Required for Windows Form Designer support

  //

  InitializeComponent();

 

  //

  // TODO: Add any constructor code after InitializeComponent call

  //

}

Here’s the Visual Basic .NET form constructor code:

Public Sub New()

  MyBase.New()

 

  'This call is required by the Windows Form Designer.

  InitializeComponent()

 

  'Add any initialization after the InitializeComponent() call

 

End Sub

In each constructor method, a call is made to a form-level method named InitializeComponent. By default, this method is hidden in both C# and Visual Basic .NET.

Here are the first few lines of code in C#:

private void InitializeComponent()

{

  this.mainMenu1 = new System.Windows.Forms.MainMenu();

  this.FilePad = new System.Windows.Forms.MenuItem();

  this.FileExitBar = new System.Windows.Forms.MenuItem();

  private System.Windows.Forms.MenuItem CustomersPad;

  private System.Windows.Forms.MenuItem CustomerOrdersBar;

  this.WindowPad = new System.Windows.Forms.MenuItem();

And here they are in Visual Basic .NET:

<System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()

Me.MainMenu1 = New System.Windows.Forms.MainMenu()

Me.FilePad = New System.Windows.Forms.MenuItem()

Me.FileExitBar = New System.Windows.Forms.MenuItem()

Me.CustomersPad = New System.Windows.Forms.MenuItem()

Me.CustomerOrdersBar = New System.Windows.Forms.MenuItem()

Me.WindowsPad = New System.Windows.Forms.MenuItem()

As you see by looking at this code, Visual Studio .NET added a form-level, private variable for the main menu and each menu item. When the form is instantiated and the constructor method calls this InitializeComponent method, an instance of each menu class is created and stored in these form-level variables.

As you look down through the rest of the code in the InitializeComponent method, you see code added by the Menu Designer and Form Designer that changes the values of menu and form properties. For example, at the bottom of each method, you find the following code setting various form properties.

In C#:

this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);

this.ClientSize = new System.Drawing.Size(640, 480);

this.IsMdiContainer = true;

this.Name = "MainAppForm";

this.Text = "My Simple WinForm Application";

this.WindowState = System.Windows.Forms.FormWindowState.Maximized;

In Visual Basic .NET:

Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)

Me.ClientSize = New System.Drawing.Size(640, 480)

Me.IsMdiContainer = True

Me.Menu = Me.MainMenu1

Me.Name = "MainAppForm"

Me.Text = "My Simple WinForm Application"

Me.WindowState = System.Windows.Forms.FormWindowState.Maximized

This approach is very different from the Visual FoxPro form and menu designers. In Visual Studio .NET, ultimately, every object you add to a form or a menu is implemented as code in the form’s associated code file. In design view, Visual Studio .NET interprets the code into a representation you work with visually on a form.

Getting back to the real reason you began looking at the form’s code, if you look at the C# code, you find the following line:

this.FileExitBar.Click += new System.EventHandler(this.FileExitBar_Click);

This code links the Exit menu bar’s Click event with the form-level FileExitBar_Click event handler method shown earlier. The FileExitBar_Click method is registered as an event handler (using the += operator) with the FileExitBar’s Click event, establishing a link between the two.

Now, look at the corresponding Visual Basic .NET code. You may be surprised to find this code is not located in the InitializeComponent method. This is because VB .NET establishes event handlers differently by using the “Handles” keyword. The code you’re looking for is actually found right in the FileExitBar_Click method declaration:

Private Sub FileExitBar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles FileExitBar.Click

 

End Sub

The Handles keyword indicates the FileExitBar_Click method handles the FileExitBar.Click event.

Now that you have a basic understanding of how this works, it’s time to add code to the event handler that closes the application when the user selects File | Exit from the menu.

Here’s the code in C#:

private void FileExitBar_Click(object sender, System.EventArgs e)

{

  this.Close();

}

And here’s the code in Visual Basic .NET:

Private Sub FileExitBar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles FileExitBar.Click

  Me.Close()

End Sub

Text Box: ¥

For more information on events, event handlers, and delegates, see Chapter 5, “Object-Orientation in C# and Visual Basic .NET”.

Now it’s time to test out your new menu. From the Build menu, select Build Solution. Next, press F5 to start the application. If everything’s working properly, your main application form should maximize to fill your computer monitor, and you should have a main menu containing File, Customers, and Window menu pads. When you select File | Exit from the menu, your application should exit. You can also exit the application by clicking the close button [X] at the top right corner of the application window.

When you add another form to the project later in this chapter, you will revisit the menu system to verify the Window menu pad is working properly.

Text Box: ¥

For more information on creating menus, see the .NET Help topic “Adding Menu Enhancements to Windows Forms”.

Adding a new form to the project

To demonstrate the capabilities of Windows Forms and Visual Studio .NET, you’ll create a “Customer Orders” form that allows you to view, edit, and create customer orders.

First, you need to add a new form to the project. Right-click on the project and select Add | Add Windows Form from the shortcut menu. In the Add New Item dialog, change the form name to “CustomerOrdersForm.cs” (in C#) or “CustomerOrdersForm.vb” (in Visual Basic .NET), and click the Open button. This opens the new form in design mode.

Go to the Properties Window and change the Size to 580, 420 (this makes the form a good size for the controls you will add later on), change the Text property to “Customer Orders”, and change the StartPosition property to “CenterScreen” (for information on the other settings of the StartPosition property, see the .NET Help topic “FormStartPosition Enumeration”). “CenterScreen” automatically centers the form in the middle of the screen.

Working with the TabControl

The .NET TabControl is equivalent to Visual FoxPro’s page frame control. I recommend adding a TabControl to the form to give yourself an idea of how to use it in your applications. The easiest way to add the control is to double-click on it in the Toolbox, which automatically adds the control to the upper left corner of the form. Next, go to the Properties Window, change the TabControl’s Name property to TabControl, change its Size to 580,420, and set its TabStop property to False.

You may have noticed that, by default, the TabControl doesn’t have any tabs. To add tabs, select the TabPages property in the Properties Window, click the ellipses button (…) to launch the TabPage Collection Editor (Figure 7), and click the Add button to add the first page. This displays a Properties sheet in the right pane of the dialog you can use to edit properties of the tab page.

Change the Text property to “List” and change the Name property to “ListPage”. Add a second tab and set its Text property to “Properties” and the Name property to “PropertiesPage”. Afterwards, click OK to close the dialog.

Figure 7. The TabPage Collection Editor allows you to add, remove, and modify tab pages in the TabControl.

Adding controls to the List page

In this section, you’ll add a few controls to the List page—a text box that allows you to enter a customer ID and a DataGrid that displays all orders for the specified customer. You can drag and drop controls any place on the form that, but if you want to end up with a form looking like the one shown in this chapter’s figures, you should set location and size of controls as specified.

First, click on the List page of the tab control. Add a label to the page by double-clicking on the Label item in the Toolbox (or by dragging and dropping it on the form). Go to the Properties Window and change the label’s Text property to “Customer ID:” and its Name property to “lblCustomerID”. Change its Location property to 25,25.

Next, add a text box control to the form from the Toolbox. Set its Name property to txtCustomerID and remove all characters from its Text property. Set the Location property to 100,22. Finally, set the text box’s CharacterCasing property to “Upper”. This causes the text box to automatically uppercase all characters as they are typed (the Customer IDs in the Northwind database are all uppercased).

Add a DataGrid control to the form, set its Name property to “grdOrders”, its Location property to 25,65, and its Size property to 425, 275.

Finally, add three Buttons to the form and set their properties as shown in Table 1.

Table 1. Properties of Customer Orders form buttons

Name

Text

Location

btnNew

&New

475, 85

btnDelete

&Delete

475, 125

btnClose

&Close

475, 285

 

When you’re done, the List page should look like Figure 8.

Figure 8. The Customer Orders form List page allows you to view orders for a specified customer.

Now add code to respond to the Close button’s click event and close the form. First double-click on the Close button, which adds a btnClose_Click event handler method to the form. You just need to add a single line of code to this method to close the form.


In C#:

private void btnClose_Click(object sender, System.EventArgs e)

{

  this.Close();

}

And in Visual Basic .NET:

Private Sub btnClose_Click(ByVal sender As System.Object, _

    ByVal e As System.EventArgs) Handles btnClose.Click

    Me.Close()

End Sub

Before adding any controls to the Properties page, I’ll first get to something a little more interesting—adding code to the form that displays all orders for a customer when the user enters a Customer ID.

Accessing data by means of business objects

Many of the .NET samples available in books, magazines, and on-line articles place data access code directly in the user interface. As mentioned in Chapter 8, “.NET Business Objects”, a far better approach is performing all data access and manipulation using business objects. In this section, you will apply what you learned in the previous chapter and create business objects to use with the Customer Orders form.

First, you need to add two files to your Windows Forms project that are found in the sample code for this book. If you’re working with C#, add the files Business.cs and Data.cs to your project. To add these files to your project, open the Solution Explorer, right-click on the project, and select Add | Add Existing item from the shortcut menu. In the Add Existing Item dialog, navigate to the directory containing these files and add them to the project.

If you’re working with Visual Basic .NET, copy the existing Business.vb and Data.vb files from the samples directory into your project’s root directory using Windows Explorer. Afterwards, add the files to your project as mentioned above. Next, change the namespace in the Business.vb file to:

Namespace Business

And change the namespace in the Data.vb file to:

Namespace Data

Now you need to create an Orders business object that retrieves data from the SQL Server Northwind database’s Orders table. To do this, open the Solution Explorer, right-click your project, and select Add | Add New Item from the shortcut menu. In the Add New Item dialog’s right pane, select Code File. Next, change the code file name to “OrdersBusiness.cs” (in C#) or “OrdersBusiness.vb” (in Visual Basic .NET) and click the Open button. This opens an empty code file in the IDE.

At the top of the code file, enter the following code to define a new Orders business object class.

In C#:

using System.Data;

 

namespace HW.NetBook.Samples.Business

{

  public class Orders : BusinessObject

  {

    public Orders()

    {

       this.TableName = "Orders";

    }

 

    // Returns a DataSet containing all orders for the specified customer

    public DataSet GetOrdersByCustomerID(string custID)

    {

       custID = custID.ToUpper();

       return this.GetDataSet("SELECT * FROM Orders WHERE CustomerID = '" +

         custID + "'");

    }

 

    /// Returns a DataSet containing the specified order

    public DataSet GetOrderByOrderID(int orderID)

    {

       return this.GetDataSet("SELECT * FROM Orders WHERE OrderID = " +

          orderID);

    }

 

  }

}

And in Visual Basic .NET:

Imports System.Data

 

Namespace Business

    Public Class Orders

        Inherits BusinessObject

 

        Public Sub New()

            Me.TableName = "Orders"

        End Sub 'New

 

 

        ' Returns a DataSet containing all orders for the specified customer

        Public Function GetOrdersByCustomerID(ByVal custID As String) _

            As DataSet

          custID = custID.ToUpper()

            Return Me.GetDataSet(("SELECT * FROM Orders WHERE CustomerID = '" _

                + custID + "'"))

        End Function 'GetOrdersByCustomerID

 

        ' Returns a DataSet containing the specified order

        Public Function GetOrderByOrderID(ByVal orderID As Integer) As DataSet

            Return Me.GetDataSet(("SELECT * FROM Orders WHERE OrderID = " & _

                orderID.ToString()))

        End Function 'GetOrderByOrderID

 

    End Class 'Orders

End Namespace 'Business

Next, add a reference to the following namespaces to the top of the code file. These references tell the compiler where to find the .NET DataSet class as well as your business object classes.

In C#:

using System.Data;

using HW.NetBook.Samples.Business;

And in Visual Basic .NET:

Imports System.Data

Imports HW.NetBook.Samples.Business

Now you need to add code to the form’s Load event that instantiates the Orders business object when the form is first loaded. Add the following private field declaration at the top of the CustomerOrdersForm class declaration. This new field will hold a reference to the Orders business object when it’s instantiated. While you’re at it, also add a private variable named dsOrder to hold the Orders DataSet returned from the Orders business object.

In C#:

public class CustomerOrdersForm : System.Windows.Forms.Form

  {

       private Orders OrdersObj;

       private DataSet dsOrders;

 And in Visual Basic .NET:

Public Class CustomerOrdersForm

    Inherits System.Windows.Forms.Form

 

    Private OrdersObj As Orders

    Private dsOrders As DataSet

Next, double-click on the form’s title bar in the Form Designer to automatically
add event handler code to the form’s code file. Next, add code that instantiates the Orders business object.

In C#:

private void CustomerOrdersForm_Load(object sender, System.EventArgs e)

{

  /// Instantiate the Orders business object

  this.OrdersObj = new Orders();

}


And in Visual Basic .NET:

Private Sub CustomerOrdersForm_Load(ByVal sender As Object, _

    ByVal e As System.EventArgs) _

    Handles MyBase.Load

 

   '/ Instantiate the Orders business object

    Me.OrdersObj = New Orders()

End Sub

Now you need to add code to call the Orders business object when a user enters a value in the Customer ID text box. In Visual FoxPro, you may have placed this code in the Valid event of the text box. In .NET, you can place this code in the text box’s Validating event.

Text Box: ¥

In .NET, user control focus events occur in the following order: Enter, GotFocus, Leave, Validating, Validated, LostFocus

The text box control’s default event is TextChanged, so you can’t double-click on the text box to automatically add event handler code to the form. If you’re using C#, you do this through the Properties Window. Make sure the Customer Orders form is displayed in design mode and select the text box. Go to the Properties Window and click the button with the lightning bolt icon (the Events button). This displays all the events for the text box.

If you double-click on the Validating event (Figure 9), VS .NET automatically adds event handler code to the form and displays the name of the event handler method in the second column of the Properties Window (in this case, txtCustomerID_Validating). Remember, in C# code is added both to the bottom of the form’s code file as well as within the “hidden” area of the form’s code. If you want to delete both sets of related code, in the Properties Window right-click on the event and select Reset from the shortcut menu. Visual Studio .NET automatically removes both sets of code for you!

Figure 9. Double-clicking on a user interface control event in the Properties Window automatically adds event handler code for that event to the form.

If you are using Visual Basic .NET you can add an event handler to your code by using the combo boxes at the top of the code editor. Just select the control in the left combo box you want to create an event handler for and select the desired event in the right combo box. When you do this, Visual Basic .NET automatically adds event handler code to your form.

At this point, you can add code to the Validating event handler that calls the Orders business object with the specified Customer ID, gets the returned DataSet, and stores it in the form-level private variable dsOrder.

Here’s the code in C#:

private void txtCustomerID_Validating(object sender,

  System.ComponentModel.CancelEventArgs e)

{

  /// Get all orders for the specified customer

  dsOrders= this.OrdersObj.GetOrdersByCustomerID(this.txtCustomerID.Text.Trim());

}

And here it is in Visual Basic .NET:

Private Sub txtCustomerID_Validating(ByVal sender As Object, _

    ByVal e As System.ComponentModel.CancelEventArgs) _

    Handles txtCustomerID.Validating

    '/ Get all orders for the specified customer

    dsOrders = Me.OrdersObj.GetOrdersByCustomerID(Me.txtCustomerID.Text.Trim())

End Sub

At this point, you have data and you have user interface controls, but now you need to bring the two together so you can display and edit the data—this requires data binding.

Data binding in Windows Forms

In Visual FoxPro, you could bind user interface controls to data by setting the ControlSource, RecordSource, or RowSource of a control to a cursor, field, or object property.

In a Windows Forms application, you can bind a user interface control to a DataSet, Array, ArrayList, or any collection that implements the IList interface (for information on implementing interfaces, see chapter 5, “Object-Orientation in C# and Visual Basic .NET”).

Simple data binding

Simple data binding involves binding a single property of a user interface control to a single data value. Controls that support simple data binding usually only display one value at a time such as text boxes, check boxes, and radio buttons. Binding a text box to the property of an object is an example of simple data binding. Another example is binding a text box to a single column in a DataTable.

Simple-bound controls contain a collection of Binding objects stored in a ControlBindingsCollection. Each binding object represents an individual property bound to an individual data source value. This collection is accessible through the control’s DataBindings property. You use the DataBindings.Add method to add a Binding object to a control dynamically at run time. You will see an example of this in the “Adding controls to the Properties tab” section later in this chapter.

Complex data binding

Complex data binding involves binding a control to a collection, such as a DataSet, Array, or ArrayList. Controls that support complex data binding can display more than one value at a time—this includes DataGrids, combo boxes, and list boxes. You will see examples of complex data binding with DataGrids and combo boxes later in this chapter.

Data binding controls at run time

You can data bind controls at either design time or run time. Visual Studio .NET has visual tools for adding a data source to a form and binding user interface controls to that data source. However, as you might imagine, using these VS .NET data binding tools creates a hard-coded monolithic application. However, this book is showing you best practices, so I won’t use these tools in this chapter, but will show you how to bind data dynamically at run time instead.

The following code sample shows the txtCustomerID_Validating method with two new lines of code (shown in grey). The first new line calls the DataGrid’s DataBindings.Clear method. This clears any existing data binding for the grid, which is necessary because this code gets run every time the user enters a new Customer ID. The second new line of code calls the grid’s SetDataBinding method, passing the Orders DataSet and the Orders business object’s TableName property (which is set to “Orders”).

In C#:

private void txtCustomerID_Validating(object sender,

  System.ComponentModel.CancelEventArgs e)

{

  /// Get all orders for the specified customer

Text Box: /// Clear all data bindings
this.grdOrders.DataBindings.Clear();

/// Data bind the grid
this.grdOrders.SetDataBinding(dsOrders, OrdersObj.TableName);

  dsOrders= this.OrdersObj.GetOrdersByCustomerID(this.txtCustomerID.Text.Trim());

 

}

And in Visual Basic .NET:

Private Sub txtCustomerID_Validating(ByVal sender As Object, _

    ByVal e As System.ComponentModel.CancelEventArgs) _

    Handles txtCustomerID.Validating

    '/ Get all orders for the specified customer

    dsOrders = Me.OrdersObj.GetOrdersByCustomerID(Me.txtCustomerID.Text.Trim())

Text Box:   '/ Clear all data bindings
  Me.grdOrders.DataBindings.Clear()
  '/ Data bind the grid
  Me.grdOrders.SetDataBinding(dsOrders, OrdersObj.TableName)
Text Box: '/ Clear all data bindings
Me.grdOrders.DataBindings.Clear()
'/ Data bind the grid
Me.grdOrders.SetDataBinding(dsOrders, OrdersObj.TableName)

 

End Sub

At this point, you’re almost ready to test what you’ve done so far, but first you need to add code to launch your form when the user selects Customers | Orders from the menu. To do this, open the MainAppForm class in design mode by double-clicking MainAppForm.cs or MainAppForm.vb file in the Solution Explorer (if it’s not already open). Next, click on the Customers menu pad and double-click the Orders menu bar below it. This adds a CustomerOrdersBar_Click event handler method to the form (if you remembered to change the name of your menu bar). Add the following code to instantiate and show the form.

In C#:

private void CustomerOrdersBar_Click(object sender, System.EventArgs e)

{

  /// Instantiate the form, set its MdiParent and show the form to the user

  CustomerOrdersForm OrdersForm = new CustomerOrdersForm();

  OrdersForm.MdiParent = this;

  OrdersForm.Show();

}

And in Visual Basic .NET:

Private Sub CustomerOrdersBar_Click(ByVal sender As System.Object, _

    ByVal e As System.EventArgs) Handles CustomerOrdersBar.Click

    '/ Instantiate the form, set its MdiParent and show the form to the user

    Dim OrdersForm As New CustomerOrdersForm()

    OrdersForm.MdiParent = Me

    OrdersForm.Show()

End Sub

The first line of code instantiates the Customer Orders form. The second line of code stores a reference to the main application form in the Order form’s MdiParent property. This is necessary if you want the Order form to be contained within the main application form. If you do not set this property, the Order form can be positioned outside the confines of the main form. The last line of code makes the form visible to the user.

Compiling and running the form

Now you’re ready to compile and run the form. You can perform each of these steps separately or you can just run the form (which is what I usually do). Press F5 (the shortcut key for the Debug | Start menu bar) and Visual Studio .NET first compiles the solution and then runs the application. If there are any errors, VS .NET displays a warning dialog asking if you still want to run the application. You should select No and fix the problems first before running it again. Although you can select Yes, since the compiler encountered an error, you will be running the last successfully compiled version of the application instead.

When the main application form appears, select Customers | Orders from the menu to launch the form. To test that the form and business object are working properly, enter the text “QUEEN” in the Customer ID box and press the Tab key. The grid should display a list of all orders for the customer (Figure 10). If everything works properly, you have permission to pump your fist in the air and do the .NET “dance of joy”.

Figure 10. Entering a Customer ID in the text box and pressing Tab sends a request to the Orders business object, which returns a DataSet that displays in the DataGrid.

While you’re still flush with the feeling of success, click the Windows menu pad to see that a menu bar for your Customer Orders form has automatically been added (Figure 11).

Figure 11. When you set a menu pad’s MdiList property to True, a menu bar is added to the pad for each form that is open in the application.

After the initial euphoria wears off, you will probably notice that the data in the grid isn’t formatted as nicely as you’d like it to be. First of all, the color scheme of the DataGrid could use some improvement, and secondly, the DataGrid shows all columns in the DataSet (which you probably don’t want to do). Select File | Exit to close down the application, and you can take care of these issues.

Designing the DataGrid

There are several very cool features available in Visual Studio .NET that simplify making your DataGrid look great, including a number of features you probably wish were in Visual FoxPro.