Binding Data to the User Interface

Display and update data:

  • Bind data to the user interface.

Data binding refers to the process of creating a link between a data model and the user interface of an application. The data model can be any source of data within the application: It might be an array of values, an Extensible Markup Language (XML) file, or data stored in a database. The user interface consists of the controls that are contained on the forms in an application.

The .NET Framework includes extremely flexible data binding capabilities. In the following sections you'll learn about many of those capabilities, including the following:

  • Simple data binding

  • Complex data binding

  • One-way and two-way data binding

  • The BindingContext object

  • The Data Form Wizard

Simple Data Binding

Simple data binding means connecting a single value from the data model to a single property of a control. For example, you might bind the Vendor object name from a list of vendors to the Text property of a TextBox control.

STEP BY STEP

5.1 Using Simple Data Binding to Display a Vendor Name

1.
Launch a new Visual C# Windows application project. Name it 315C05.

2.
Place a TextBox control on a new Windows form. Name the control txtVendorName.

3.
Double-click the form and enter the following code in the form's Load event:

private void StepByStep5_1_Load(object sender,
    System.EventArgs e)
{
    // Create an array of vendor names
    String [] astrVendorNames =
       {"Microsoft", "Rational", "Premia"};

    // Bind the array to the text box
    txtVendorName.DataBindings.Add(
        "Text", astrVendorNames, "");
}

4.
Insert the Main() method to launch the form. Set the form as the startup object for the project.

5.
Run the project. The text box is now bound to the array, and it displays the first value from the array.


Looking at the code in Step by Step 5.1, you can see that the .NET Framework supplies an object model for binding. The control has a ControlBindingsCollection (accessed through its DataBindings property), which contains instances of the Binding class. By default there are no Binding objects in the collection, so when you create a control, it's not bound. To bind the control, you can add a new Binding object to the ControlBindingsCollection by using its Add() method.

Because the Add() method creates a new instance of the Binding class, it takes the same parameters as the constructor for that class:

  • The name of the property to bind to

  • The data source to bind

  • The navigation path to the particular data; in this particular example, the navigation path is empty because there's only one thing to bind to in this array

Now that you've seen simple data binding in action, it's time to explore the topic in a bit more depth. I'll start by looking at which entities can be bound to the user interface. Then I'll talk about which properties you can bind to those entities.

Finally, I'll explain the architecture that the .NET Framework uses to manage simple data binding. You'll see that you can work directly with the objects that handle the data binding connections if you need to interact with the data binding process from code.

Bindable Entities

In Step by Step 5.1, the Text property of the TextBox control is bound to an element from an array. The Binding class can accept many other types of data sources, including the following:

  • An instance of any class that implements the IBindingList or ITypedList interface, including the DataSet, DataTable, DataView, and DataViewManager classes.

  • An instance of any class that implements the IList interface on an indexed collection of objects. In particular, this applies to classes that inherit from System.Array, including C# arrays.

  • An instance of any class that implements the IList interface on an indexed collection of strongly typed objects. For example, you can bind to an array of Vendor objects.

Binding to a collection of strongly typed objects is a convenient way to handle data from an object-oriented data model.

STEP BY STEP

5.2 Using Simple Data Binding with a Strongly Typed IList

1.
Add a new class named Vendor.cs to your project. Enter this code in the class:

using System;
namespace _316C05
{
    public class Vendor
    {
        private string vendorName;
        public string VendorName
        {
            get
            {
                return vendorName;
            }
            set
            {
                vendorName = value;
            }
        }
        public Vendor(string strVendorName)
        {
            this.VendorName = strVendorName;
        }
    }
}

2.
Place a TextBox control on a new Windows form. Name the control txtVendorName.

3.
Double-click the form and enter the following code in the form's Load event handler:

private void StepByStep5_2_Load(
    object sender, System.EventArgs e)
{
    // Create an array of vendor objects
    Vendor[] aVendors = new Vendor[3];
    aVendors[0] = new Vendor("Microsoft");
    aVendors[1] = new Vendor("Rational");
    aVendors[2] = new Vendor("Premia");

    // Bind the array to the text box
    txtVendorName.DataBindings.Add(
       "Text", aVendors, "VendorName");
}

NOTE

Navigation Path for Objects Step by Step 5.2 demonstrates how you can use the navigation path (the third parameter to the DataBindings.Add call) to specify a particular property of an object to bind to a control.

4.
Insert the Main() method to launch this form. Set the form as the startup object for the project.

5.
Run the project. The text box is now bound to the array, and it displays the first value from the array.


At this point, you could be forgiven for thinking that binding makes only the first element of a data source available on the user interface. In fact, bound controls are designed to let the user move through an entire collection of data. Later in this chapter you'll learn the details of the BindingContext object, which enables you to manipulate the data behind a bound control. But as a preview, Figure 5.1 shows a form that lets you scroll through the data in a strongly typed IList.

Figure 5.1. This form includes a bound TextBox control and two Button controls that let the user scroll through the data.


STEP BY STEP

5.3 Scrolling Through Data, Using the BindingContext Object

1.
Place a TextBox control on a new Windows form. Name the control txtVendorName.

2.
Place a Button control on the form. Name the control btnPrevious and set its Text property to <.

3.
Place a second Button control on the form. Name the control btnNext and set its Text property to >.

4.
Double-click the form and enter the following code:

// Create an array of vendor objects
Vendor aVendors = new Vendor[3];

private void StepByStep5_3_Load(object sender,
     System.EventArgs e)
{
    // Initialize the vendors array
    aVendors[0] = new Vendor("Microsoft");
    aVendors[1] = new Vendor("Rational");
    aVendors[2] = new Vendor("Premia");

    // Bind the array to the text box
    txtVendorName.DataBindings.Add(
       "Text", aVendors, "VendorName");
}

5.
Add the following code to handle the Click events from the two buttons:

private void btnPrevious_Click(object sender,
     System.EventArgs e)
{
    // Move to the previous item in the data source
    this.BindingContext[aVendors].Position -= 1;
}

private void btnNext_Click(object sender,
     System.EventArgs e)
{
    // Move to the next item in the data source
    this.BindingContext[aVendors].Position += 1;
}

6.
Insert the Main() method to launch this form. Set the form as the startup object for the project.

7.
Run the project. The text box is now bound to the array, and it displays the first value from the array. You can use the two buttons to move to different values within the array.


Properties That Can Be Bound

Just as the .NET Framework allows flexibility in the source of bound data, it allows flexibility on the user interface side of the equation. You can use simple data binding with any control that has a DataBindings property, which includes any control that is derived from System.Windows.Forms.Control. In practice, that means almost any control you can drop on a Windows form.

You can bind just about any property of these bindable controls to an item of data. This gives you enormous flexibility in building a user interface that depends on data. For example, you can bind an array of DateTime values to the Value property of a DateTimePicker control.

STEP BY STEP

5.4 Binding Data to a DateTimePicker Control

1.
Place a DateTimePicker control on a new Windows form. Name the control dtBound.

2.
Place a Button control on the form. Name the control btnPrevious and set its Text property to <.

3.
Place a second Button control on the form. Name the control btnNext and set its Text property to >.

4.
Double-click the form and enter the following code:

// Create an array of dates
DateTime [] adtBound =
 {DateTime.Today , DateTime.Today.AddDays(1),
 DateTime.Today.AddDays(2), DateTime.Today.AddDays(3),
DateTime.Today.AddDays(4), DateTime.Today.AddDays(5)};

private void StepByStep5_4_Load(object sender,
    System.EventArgs e)
{
    // Bind the array to the date/time picker
    dtBound.DataBindings.Add("Value", adtBound, "");
}

5.
Add the following code to handle the Click events from the two buttons:

private void btnPrevious_Click(
    object sender, System.EventArgs e)
{
    // Move to the previous item in the data source
    this.BindingContext[adtBound].Position -= 1;
}

private void btnNext_Click(
    object sender, System.EventArgs e)
{
    // Move to the next item in the data source
    this.BindingContext[adtBound].Position += 1;
}

6.
Insert the Main() method to launch the form. Set the form as the startup object for the project.

7.
Run the project. The DateTimePicker control is now bound to the array, and it displays the first value from the array. You can use the two buttons to move to different values within the array.


If you think creatively, you can find many ways to use simple data binding beyond simply displaying text in a text box. Here are some possibilities:

  • Display a set of photos by binding them to the Image property of a PictureBox control.

  • Show the relative magnitude of quantities with the Value property of a ProgressBar control.

  • Color-code an area of a form by binding to the BackColor property of a Panel control.

The Architecture of Simple Data Binding

When you use simple data binding to connect a control to a data source, the .NET Framework creates a pair of objects to manage the binding: a CurrencyManager object and a BindingContext object. Depending on the number of controls on the form and the data to which they are bound, a single form might involve several of each of these objects.

The CurrencyManager object is responsible for keeping track of which piece of data from a data source is currently bound to the user interface. Although so far you've only seen one data bound control on each form, a single form can contain multiple bound controls. If all the controls are bound to the same data source, they can share a CurrencyManager object. But a single form can involve multiple CurrencyManager objects as well. Suppose, for example, that you built a form with an array of Vendor objects bound to one control and an array of DateTime objects bound to another control. In that case, the form would have two CurrencyManager objects.

Any bound form will also have at least one BindingContext object. The job of the BindingContext object is to keep track of the various CurrencyManager objects on the form. The indexer of the BindingContext object returns a CurrencyManager object. Consider this line of code:

this.BindingContext[adtBound].Position += 1;

That tells the BindingContext object for the form to return a CurrencyManager object for the adtBound data source and then to increment the Position property of the CurrencyManager object. The CurrencyManager object encapsulates the knowledge of how to move the pointer within the data array when the Position property is changed.

Like forms, container controls (such as the GroupBox, Panel, or TabControl controls) can have their own BindingContext objects. By using these separate BindingContext objects, you can create forms that have independently scolling views of the same data.

You'll learn about the CurrencyManager object in more depth later in this chapter. But first, let's look at another variety of data binding: complex data binding.

REVIEW BREAK

  • Simple data binding refers to connecting a single entity in the data model to a single property of a control on the user interface.

  • Any class that implements the IBindingList, ITypedList, or IList interface can deliver data via simple data binding.

  • You can bind to almost any property of any control.

  • A form control uses CurrencyManager and BindingContext objects to keep track of data binding.


Complex Data Binding

In complex data binding, you bind a user interface control to an entire collection of data, rather than to a single data item. A good example of complex data binding involves the DataGrid control. Figure 5.2 shows a bound DataGrid control displaying data from the Suppliers table in the SQL Server 2000 Northwind database.

Figure 5.2. By using complex data binding, you can see an entire collection of data on the user interface at one time.


EXAM TIP

Database Terminology In some literature you'll see the rows of a database table referred to as records or tuples and the columns referred to as fields or attributes.


You'll learn how to build this form later in the chapter, but for now, just concentrate on its features. DataGrid is a single control that displays many pieces of data. In this case, the data is taken from the rows and columns of the Suppliers table in a SQL Server 2000 database. You can click any cell in the DataGrid control and edit the data that the cell contains. If the form is properly programmed, these edits are reflected in the underlying data.

NOTE

The Northwind Sample Database Whenever I've used data from a database in this book, I've used the Northwind sample database that comes as part of SQL Server 2000. Visual Studio .NET includes Microsoft Data Engine (MSDE), a stripped-down version of SQL Server that you can use if you don't have the full version installed. See your Visual Studio CD's readme file for information on installing MSDE. You can also find the Northwind sample database in any version of Microsoft Access, but the Access version does not ship with Visual Studio .NET. The code in this book assumes that you are using the SQL Server version.


Obviously, complex data binding is a powerful tool for transferring large amounts of data from a data model to a user interface. The following sections dig into the mechanics of complex data binding with two examples:

  • Binding to a ComboBox or ListBox control

  • Binding to a DataGrid control

Binding to a ComboBox or ListBox Control

The ComboBox and ListBox controls both provide ways for the user to select one item from a list of data. The difference between the two is that in the ListBox control, the list is visible at all times, whereas in the ComboBox control, the list is hidden until the user clicks the drop-down arrow at the end of the box. Either of these controls can be loaded with an entire list of data via complex data binding.

STEP BY STEP

5.5 Binding Data to a List in a ListBox Control

1.
Place a ListBox control on a new Windows form. Name the control lbExams.

2.
Add a new class named Exam.cs to the project. Enter this code in the class:

using System;
namespace _316C05
{
    public class Exam
    {
        private String examNumber;
        private String examName;

        public String ExamNumber
        {
            get
            {
                return examNumber;
            }
        }

        public String ExamName
        {
            get
            {
                return examName;
            }
        }
        public Exam(String strExamNumber,
            String strExamName)
        {
            examNumber = strExamNumber;
            examName = strExamName;
        }
    }
}

3.
Double-click the form and enter the following code in the form's Load event handler:

private void StepByStep5_5_Load(object sender,
     System.EventArgs e)
{
    // Create an array of exams
    Exam[] aExams =
    {
    new Exam("315",
       "Web Applications With Visual C# .NET"),
    new Exam("316",
       "Windows Applications With Visual C# .NET"),
    new Exam("320", "XML With Visual C# .NET"),
    new Exam("305", "Web Applications With VB.NET"),
    new Exam("306",
        "Windows Applications With VB.NET"),
    new Exam("310", "XML With VB.NET")};

    // Bind the array to the list box
    lbExams.DataSource = aExams;
    lbExams.DisplayMember = "ExamName";
}

4.
Insert the Main() method to launch the form. Set the form as the startup object for the project.

5.
Run the project. The ListBox control displays the ExamName property of every object in the array.


Step by Step 5.5 works by first creating an array of exam objects that contains all the information you'd like to display in the list portion of a ListBox control. It then sets the DataSource property of the ListBox control to the name of the array and the DisplayMember property to the name of the object property that supplies the text for the list. The result is a ListBox control that allows the user to choose from a list of exam names.

As it stands, Step by Step 5.5 doesn't do anything with the data after the user chooses an item from the list. But frequently you'll use a complex data-bound ListBox or ComboBox control in conjunction with a simple data-bound control such as a TextBox control. By selecting a row in the ListBox control, the user can choose a value for the TextBox control.

You can think of the ListBox control in this case as a little pump that moves data from one part of the data model to another. Step by Step 5.6 shows how you can set this up.

STEP BY STEP

5.6 Using a ListBox Control with Two Data Bindings

1.
Start with the form from Step by Step 5.5. Add two Label controls, two Button controls, and two TextBox controls. Name the TextBox controls txtCandidateName and txtExamNumber. Name the button controls btnPrevious and btnNext. Arrange the controls as shown in Figure 5.3.

Figure 5.3. You can design a form with a bound ListBox control and other controls to see the effects of data binding.


2.
Add a new class named Candidate.cs to the project. Enter this code in the class:

using System;
namespace _316C05
{
    public class Candidate
    {
        private string examNumber;
        private string candidateName;

        public string ExamNumber
        {
            get
            {
                return examNumber;
            }
            set
            {
                examNumber = value;
            }
        }

        public string CandidateName
        {
            get
            {
                return candidateName;
            }
            set
            {
                candidateName = value;
            }
        }

        public Candidate(String strCandidateName,
           String strExamNumber)
        {
            this.CandidateName = strCandidateName;
            this.ExamNumber = strExamNumber;
        }
    }
}

3.
Attach event handlers to the form's Load event and to the btnPrevious and btnNext controls' Click events. Add the following code in the event handlers:

// Create an array of candidates
private Candidate[] aCandidates = {
              new Candidate("Bill Gates", "305"),
              new Candidate("Steve Ballmer", "320")};

private void StepByStep5_6_Load(object sender,
   System.EventArgs e)
{
    // Create an array of exams

    Exam[] aExams = {
    new Exam("315",
       "Web Applications With Visual C# .NET"),
    new Exam("316",
       "Windows Applications With Visual C# .NET"),
    new Exam("320", "XML With Visual C# .NET"),
    new Exam("305", "Web Applications With VB.NET"),
    new Exam("306",
       "Windows Applications With VB.NET"),
    new Exam("310", "XML With VB.NET")};

    // Bind the array to the list box
    lbExams.DataSource = aExams;
    lbExams.DisplayMember = "ExamName";
    lbExams.ValueMember = "ExamNumber";

    // Bind the candidates to the text boxes
    txtCandidateName.DataBindings.Add(
        "Text", aCandidates, "CandidateName");
    txtExamNumber.DataBindings.Add(
        "Text", aCandidates, "ExamNumber");

    // And bind the exam number to the list box value
    lbExams.DataBindings.Add(
        "SelectedValue", aCandidates, "ExamNumber");
}

private void btnPrevious_Click(object sender,
     System.EventArgs e)
{
       this.BindingContext[aCandidates].Position -= 1;
}

private void btnNext_Click(object sender,
    System.EventArgs e)
{
       this.BindingContext[aCandidates].Position += 1;
}

4.
Insert the Main() method to launch the form. Set the form as the startup object for the project.

5.
Run the project. As you move through the Candidate records, the ListBox control shows the exam name that matches the exam number for the candidate. If you change the value in the ListBox control or in the txtExamNumber control, the change is reflected in the other control as soon as the change is committed (that is, when you tab to another control).


Understanding Step by Step 5.6 is crucial for the effective use of ComboBox and ListBox controls in applications. Using these controls can be a little tricky because the ListBox control is bound to two different things. Here's a review of how it all fits together:

  • The ListBox control in Step by Step 5.6 draws the list of items to display from the array of Exam objects. The list portion of the list box is complex-data-bound to this array. The complex binding is managed by setting the DataSource, DisplayMember, and ValueMember properties of the ListBox control.

  • The two TextBox controls are simple-data-bound to different elements in the array of Candidate objects. As you move through that array (with the btnNext and btnPrevious controls), the data displayed in those TextBox controls changes. This is, by the way, a two-way link. If you change the data in one of the TextBox controls, it's also changed in the array; you can see this by changing an entry, scrolling to another candidate, and then scrolling back.

  • The SelectedValue property of the ListBox control is also simple-data-bound to an element in the array of Candidate objects. This sets up the link between the two arrays. As you choose an item from the list in the ListBox control, your choice is automatically pushed to the bound value from the Candidate array.

  • Because the SelectedValue property of the ListBox control and the Text property of the txtExamNumber control are bound to the same value, they automatically stay in sync when either one is updated.

  • You can use the DisplayMember and ValueMember properties of the ListBox control to cause it to show one value while binding another, as in Step by Step 5.6.

Binding to a DataGrid Control

The DataGrid control provides a way to display many rows from a data model at one time. The DataGrid control is designed to let you see an entire collection of data (often called a resultset) at one time.

STEP BY STEP

5.7 Binding an Array of Objects to a DataGrid Control

1.
Place a DataGrid control on a new Windows form. Name the control dgExams.

2.
Double-click the form and enter the following code in the form's Load event handler:

private void StepByStep5_7_Load(object sender,
    System.EventArgs e)
{
    // Create an array of exams
    Exam[] aExams ={
      new Exam("315",
          "Web Applications With Visual C# .NET"),
      new Exam("316",
          "Windows Applications With Visual C# .NET"),
      new Exam("320", "XML With Visual C# .NET"),
      new Exam("305", "Web Applications With VB.NET"),
      new Exam("306",
           "Windows Applications With VB.NET"),
      new Exam("310", "XML With VB.NET")};

    // Bind the array to the data grid
    dgExams.DataSource = aExams;
}

3.
Insert the Main() method to launch the form. Set the form as the startup object for the project.

4.
Run the project. The DataGrid control displays all the information from the aExams array.


The DataGrid control is a mainstay of data display for Visual C# .NET forms. As such, it is extremely configurable. Visual Studio .NET includes two interfaces for setting the display propeties of a DataGrid control. First, you can set individual properties to control the look of the DataGrid control in the Properties window. Second, you can use AutoFormats to quickly apply a whole new look to a DataGrid control.

STEP BY STEP

5.8 Applying an AutoFormat to a DataGrid Control

1.
Select a DataGrid control on a Visual C# .NET form with the form open in the Windows Forms Designer.

2.
Click the AutoFormat hyperlink, which is located directly under the properties list in the Properties window. This opens the Auto Format dialog box.

3.
Select a format from the list and click OK to apply the new format to the DataGrid control. Figure 5.4 displays three DataGrid controls on a form, with three different AutoFormats applied. Although the controls are each bound to the same array of objects, they all look different.

Figure 5.4. The three DataGrid controls on this form have had three different AutoFormats applied.



When you need more precise formatting for a DataGrid control than the Auto Format dialog box allows, or when you just don't care for the look of any of the AutoFormats, you can set individual display properties for the DataGrid control. Table 5.1 lists the properties you can use to control the look of the DataGrid control.

Table 5.1. DataGrid Control Display Properties
PropertyDescription
AlternatingBackColorSpecifies the background color to use for even-numbered rows in the grid.
BackColorSpecifies the background color to use for odd-numbered rows in the grid.
BackgroundColorSpecifies the color to use for any portion of the control that's not filled with data.
BorderStyleOffers the choices None, FixedSingle, and Fixed3D for the borders of the control.
CaptionBackColorSpecifies the background color for the caption portion of the control.
CaptionFontSpecifies the font for the caption portion of the control.
CaptionTextSpecifies the text to display in the caption portion of the control.
CaptionVisibleControls whether a caption will be displayed. This is a Boolean property.
ColumnHeadersVisibleControls whether each column will have a header. This is a Boolean property.
FlatModeControls whether the grid will have a 3D or a flat appearance. This is a Boolean property.
FontSpecifies the font for text in the control.
ForeColorSpecifies the foreground color for text in the control.
GridlineColorSpecifies the color for the lines of the grid.
GridlineStyleOffers the choices None and Solid.
HeaderBackColorSpecifies the background color for column and row headers.
HeaderFontSpecifies the font for column and row headers.
HeaderForeColorSpecifies the foreground color for column and row headers.
LinkColorSpecifies the color to use for hyperlinks between sections of a control.
ParentRowBackColorSpecifies the background color to use for the parent rows area.
ParentRowsForeColorSpecifies the text color to use for the parent rows area.
ParentRowsLabelStyleOffers the choices None, TableName, ColumnName, and Both.
ParentRowsVisibleControls whether the parent rows area will be visible. This is a Boolean property.
PreferredColumnWidthSpecifies the default width for columns, in pixels.
PreferredRowHeightSpecifies the Default height for rows, in pixels.
RowHeadersVisibleControls whether each row will have a header. This is a Boolean property.
RowHeaderWidthSpecifies the default width of row headers, in pixels.
SelectionBackColorSpecifies the background color for any selected cells.
SelectionForeColorSpecifies the text color for any selected cells.

Figure 5.5 shows a complex-data-bound DataGrid control that displays data from several database tables at one time, to help you understand where each of the areas mentioned in Table 5.1 is located. You'll learn how to bind database tables to the DataGrid control later in this chapter.

Figure 5.5. This figure displays the location of various formatting areas on a DataGrid control.


GUIDED PRACTICE EXERCISE 5.1

In this exercise, you'll be working with employee data from Skylark Spaceways. This exercise helps you review the basic syntax of both simple and complex data binding, as well as the use of the DataGrid control. Table 5.2 shows the data that you need to manage.

Table 5.2. Skylark Spaceways Employee Roster
Employee NumberEmployee NamePositionHome Planet
1E.E. SmithCEOEarth
2Melanie “Jets” RiggsChief PilotMars
3William DanforthPilotMars
4Blaise CantonEngineerLuna
5Amanda TimmelCFOEarth

You need to display this data in two different ways. First, you should create a form that displays information about one employee at a time, with buttons to scroll through the list. To save space, the form should show the employee names and positions in TextBox controls.

However, it should also make the home planet information available as a ToolTip on the employee names text box. There should be a button on this form to open a second form. The second form should display the entire employee roster in grid form.

How would you create such a form?

You should try working through this problem on your own first. If you get stuck, or if you'd like to see one possible solution, follow these steps:

1.
Create a form that is populated with two TextBox controls (txtEmployeeName and txtPosition), two Label controls, and three Button controls (btnPrevious, btnNext, and btnRoster) arranged as shown in Figure 5.6. Also add a ToolTip control to the form.

Figure 5.6. The design of a form that displays employee data.


2.
Create a new class named Employee.cs in the project, using this code:

using System;
namespace _316C05
{
    public class Employee
    {
        private Int32 employeeNumber;
        private String employeeName;
        private String position;
        private String homePlanet;

        public Int32 EmployeeNumber
        {
            get
            {
                return employeeNumber;
            }
            set
            {
                employeeNumber = value;
            }
        }

        public String EmployeeName
        {
            get
            {
                return employeeName;
            }
            set
            {
                employeeName = value;
            }
        }
        public String Position
        {
            get
            {
                return position;
            }
            set
            {
                position=value;
            }
        }
        public String HomePlanet
        {
            get
            {
                return homePlanet;
            }
            set
            {
                homePlanet=value;
            }
        }
        public Employee(Int32 intEmployeeNumber,
             String strEmployeeName,
             String strPosition,
             String strHomePlanet)
        {
            EmployeeNumber = intEmployeeNumber;
            EmployeeName = strEmployeeName;
            Position = strPosition;
            HomePlanet = strHomePlanet;
        }
    }
}

3.
Double-click the form, btnPrevious, btnNext, and btnRoster to attach event handlers to the default events. Add the following code to handle data creation, data binding, and navigation in the event handlers:

// Create and stock an array of data
Employee[] aEmployees = {
     new Employee(1, "E.E. Smith", "CEO", "Earth"),
     new Employee(2, "Melanie "Jets" Riggs",
        "Chief Pilot", "Mars"),
    new Employee(3, "William Danforth",
        "Pilot", "Mars"),
     new Employee(4, "Blaise Canton",
        "Engineer", "Luna"),
     new Employee(5, "Amanda Timmel",
        "CFO", "Earth")};

private void GuidedStepByStep5_1_Load(object sender,
    System.EventArgs e)
{
    // Bind data to the ui
    txtEmployeeName.DataBindings.Add(
        "Text", aEmployees, "EmployeeName");
    txtEmployeeName.DataBindings.Add(
        "Tag", aEmployees, "HomePlanet");
    txtPosition.DataBindings.Add(
        "Text", aEmployees, "Position");

    // Transfer the home planet info to the tooltip
    ToolTip1.SetToolTip(txtEmployeeName,
       txtEmployeeName.Tag.ToString());
}

private void btnPrevious_Click(object sender,
     System.EventArgs e)
{
    this.BindingContext[aEmployees].Position -= 1;
    ToolTip1.SetToolTip(txtEmployeeName,
        txtEmployeeName.Tag.ToString());
}

private void btnNext_Click(object sender,
    System.EventArgs e)
{
    this.BindingContext[aEmployees].Position += 1;
    ToolTip1.SetToolTip(txtEmployeeName,
       txtEmployeeName.Tag.ToString());
}

private void btnRoster_Click(object sender,
     System.EventArgs e)
{
    GuidedStepByStep5_1a f =
       new GuidedStepByStep5_1a();
    f.dgEmployees.DataSource = aEmployees;
    f.Show();
}
										

EXAM TIP

Quotes in Strings If you want to insert a quote mark (") into a string constant, you can escape the quote marks with the backslash character. For instance, in the preceding example, if you would like a string to mean exactly "Melanie "Jets" Riggs", including the quotes, you would write "Melanie "Jets" Riggs". Another way to ensure verbatim representation of a string is to prefix the string with the @ sign, so the name could also be written as @"Melanie "Jets" Riggs". You also can escape out other special characters, such as the single quote and backslash, using the and @ characters, as shown previously.

4.
Create a second form and name it GuidedStepByStep5_1a (or give it any name you like and revise the code in the btnRoster_Click event handler in step 3 to match). Place a single DataGrid control on this form. Name the DataGrid control dgEmployees and set its Modifiers property to Internal.

5.
Insert the Main() method to launch the first form. Set the first form as the project's startup form and test your work. You should be able to scroll through employees, view the home planet information in a ToolTip, and open a grid that contains all the employee information.

If you have difficulty following this exercise, review the sections “Simple Data Binding” and “Complex Data Binding,” earlier in this chapter. The text and examples should help you relearn this material and help you understand what happens in this exercise. After doing that review, try this exercise again.


REVIEW BREAK

  • Complex data binding binds a user interface control to an entire collection of data.

  • To use complex data binding with a ListBox or ComboBox control, you set the control's DataSource and DisplayMember properties.

  • A ListBox or ComboBox control can act to pull values from one data source and place them in another.

  • You can cause a ListBox or ComboBox control to display one value while binding to another by using the DisplayMember and ValueMember properties of the control.

  • The DataGrid control displays an entire array of data in rows and columns. You specify the data to display by setting the DataSource property of the DataGrid control.

  • The properties of the DataGrid control include many flexible formatting options.


One-Way and Two-Way Data Binding

Data binding in Windows forms can be one-way or two-way. In one-way data binding, the bound property of the control reflects changes to the data model, but changes to the control are not written back to the data model. For example, if you display a list of customers in a ComboBox control, the act of selecting a customer from the ComboBox control does not do anything to modify the list.

In two-way data binding, changes to the control are written back to the data model. For example, if you have a TextBox control that is bound to the CustomerName property of a Customer object, changing the text in the TextBox control changes the corresponding property of the object.

NOTE

Using the DataAdapter Object to Update a Data Source To transmit changes from a data model back to the original data source, you usually use the Update() method of the DataAdapter object. For more detailed information on the DataAdapter object, see Chapter 6 and the section “Using the Data Form Wizard,” later in this chapter.


Simple data binding on Windows forms is automatically two-way. Any changes you make to the data on the form are automatically transmitted back to the data model. However, it's important to note that the data model might not be the ultimate data source. The most common exception to this occurs when you use the ADO.NET classes to access data from a database and place it in a data model. Changes to bound data on a form are written back to the data model, but they are not automatically returned to the database (though you can write code to do this).

The BindingContext and CurrencyManager Classes

You saw the BindingContext and CurrencyManager classes earlier in the chapter, in the discussion of the overall architecture of data binding. Now that you've seen both simple and complex data binding in action, it's time to look at the BindingContext and CurrencyManager classes in somewhat more detail.

The BindingContext class exists primarily as a means to retrieve the CurrencyManager objects on a form. Table 5.3 shows the important interface members of the BindingContext class. In many applications, you can let Visual C# .NET manage these objects for you. But there are times when it's useful to work directly with these objects. For instance, the CurrencyManager class provides event hooks that let you react to modifications that the user makes to bound data.

Table 5.3. Important Members of the BindingContext Class
MemberTypeDescription
Contains()MethodIndicates whether the BindingContext object contains a specific BindingManagerBase object

The BindingManagerBase class is an abstract class that is implemented in both the CurrencyManager class and the PropertyManager class. You've already seen the CurrencyManager class. The PropertyManager class is used to manipulate the current value of an individual property, rather than the property of the current object in a list; you are unlikely to have any reason to use the PropertyManager class yourself. Table 5.4 shows the important interface members of the BindingManagerBase class.

Table 5.4. Important Members of the BindingManagerBase Class
MemberTypeDescription
AddNew()MethodAdds a new object to the underlying list
BindingsPropertyGets the collection of bindings being managed by the class
CancelCurrentEdit()MethodCancels any edits that are in progress
CountPropertyGets the number of rows managed by the class
CurrentPropertyGets the current object
CurrentChangedEventOccurs when the bound value changes
EndCurrentEdit()MethodCommits any edits that are in progress
GetItemProperties()MethodGets a list of item properties for the current object in the list
PositionPropertyGets or sets the position in the underlying list that is bound with this class
PositionChangedEventOccurs when the Position property changes
RemoveAt()MethodRemoves the object at the specified position from the underlying list
ResumeBinding()MethodResumes data binding
SuspendBinding()MethodSuspends data binding

The CurrencyManager class implements most of the interfaces of the BindingManagerBase class, and it includes a few others of its own. Table 5.5 lists the important interface members of the CurrencyManager class.

Table 5.5. Important Members of the CurrencyManager Class
MemberTypeDescription
AddNew()MethodAdds a new object to the underlying list
BindingsPropertyGets the collection of bindings being managed by the class
CancelCurrentEdit()MethodCancels any edits that are in progress
CountPropertyGets the number of rows managed by the class
CurrentPropertyGets the current object
CurrentChangedEventOccurs when the bound value changes
EndCurrentEdit()MethodCommits any edits that are in progress
GetItemProperties()MethodGets a list of item properties for the current object in the list
ItemChangedEventOccurs when the current item has been altered
ListPropertyGets the IList interface from the data source
PositionPropertyGets or sets the position in the underlying list that is bound with the class
PositionChangedEventOccurs when the Position property changes
Refresh()MethodRepopulates the bound controls
RemoveAt()MethodRemoves the object at the specified position from the underlying list
ResumeBinding()MethodResumes data binding
SuspendBinding()MethodSuspends data binding

EXAM TIP

Understanding the Binding Objects You don't need to memorize every detail of the BindingContext, BindingManagerBase, and CurrencyManager objects. Instead, concentrate on knowing the overall uses of these objects. The BindingContext class is your hook to retrieve the CurrencyManager object. The BindingManagerBase class supplies the Position property that lets you see where you are in a set of bound data. The CurrencyManager class supplies the event hooks that let you interact with user-initiated data changes.


In addition to manipulating the Position property, you'll likely find the CurrencyManager class most useful for managing events on data-bound forms. Of course, because this class doesn't have a visual, control-based representation, you need to set up these events in code. Listing 5.1 shows how you can create delegates and respond to the events of the CurrencyManager class.

Listing 5.1. Trapping Events for the CurrencyManager Class
// Create an array of vendor objects
Vendor[] aVendors = new Vendor[3];

private void Listing5_1_Load(object sender,
    System.EventArgs e)
{
    // Initialize the vendors array
    aVendors[0] = new Vendor("Microsoft");
    aVendors[1] = new Vendor("Rational");
    aVendors[2] = new Vendor("Premia");

    // Bind the array to the text box
    txtVendorName.DataBindings.Add(
       "Text", aVendors, "VendorName");

    CurrencyManager cm =
      (CurrencyManager) this.BindingContext[aVendors];

    cm.CurrentChanged += new EventHandler(
         CurrencyManager_CurrentChanged);
    cm.ItemChanged += new ItemChangedEventHandler(
         CurrencyManager_ItemChanged);
    cm.PositionChanged += new EventHandler(
         CurrencyManager_PositionChanged);
}

private void CurrencyManager_CurrentChanged(Object o,
     EventArgs e)
{
    lbEvents.Items.Add("CurrentChanged");
}

private void CurrencyManager_ItemChanged(Object o,
     System.Windows.Forms.ItemChangedEventArgs icea)
{
    lbEvents.Items.Add("ItemChanged");
}

private void CurrencyManager_PositionChanged(Object o,
    System.EventArgs ea)
{
    lbEvents.Items.Add("PositionChanged");
}

private void btnPrevious_Click(object sender,
    System.EventArgs e)
{
    // Move to the previous item in the data source
    this.BindingContext[aVendors].Position -= 1;
}

private void btnNext_Click(object sender,
    System.EventArgs e)
{
    // Move to the previous item in the data source
    this.BindingContext[aVendors].Position += 1;
}

EXAM TIP

An Event Mnemonic You can remember that the CurrentChanged event refers to the user interface changes by noting that current and control begin with the same letter.


The names of the CurrencyManager class events may be a bit confusing. The ItemChanged event is fired when the data itself is changed by an external factor. For example, if you modify the data in an array and that array is bound to the user interface, the ItemChanged event occurs. The CurrentChanged event occurs when the data is changed on the user interface. This is true whether the user changes the data by typing a control or the CurrencyManager changes the data by responding to a change of the Position property. Finally, the PositionChanged event occurs when the Position property is changed. In practice, you see a CurrentChanged event whenever you see a PositionChanged event, but you can also get CurrentChanged events without a change in the Position property.

STEP BY STEP

5.9 Using CurrencyManager Events

1.
Create a form that is populated with two TextBox controls (txtEmployeeName and txtPosition), two Label controls, and two Button controls (btnPrevious and btnNext). Also add a ToolTip control to the form. Figure 5.7 shows the form in design mode.

Figure 5.7. You can use CurrencyManager events to synchronize the ToolTip of the employee name TextBox control with its text.


2.
Double-click the form and the btnPrevious and btnNext controls to attach event handlers to their default events. Enter the following code behind the form:

// Create and stock an array of data
Employee[] aEmployees = {
    new Employee(1, "E.E. Smith", "CEO", "Earth"),
    new Employee(2, "Melanie "Jets" Riggs",
        "Chief Pilot", "Mars"),
    new Employee(3, "William Danforth",
        "Pilot", "Mars"),
    new Employee(4, "Blaise Canton",
        "Engineer", "Luna"),
    new Employee(5, "Amanda Timmel", "CFO", "Earth")};

private void StepByStep5_9_Load(object sender,
      System.EventArgs e)
{
    // Bind data to the ui
    txtEmployeeName.DataBindings.Add(
       "Text", aEmployees, "EmployeeName");
    txtEmployeeName.DataBindings.Add(
        "Tag", aEmployees, "HomePlanet");
    txtPosition.DataBindings.Add(
        "Text", aEmployees, "Position");

    // Transfer the home planet info to the tooltip
    ToolTip1.SetToolTip(txtEmployeeName,
        txtEmployeeName.Tag.ToString());

    // Set up an event to update the tooltip
    CurrencyManager cm =
    (CurrencyManager) this.BindingContext[aEmployees];
    cm.PositionChanged +=
    new EventHandler(CurrencyManager_PositionChanged);
}

private void btnPrevious_Click(object sender,
     System.EventArgs e)
{
    this.BindingContext[aEmployees].Position -= 1;
}

private void btnNext_Click(object sender,
    System.EventArgs e)
{
    this.BindingContext[aEmployees].Position += 1;
}

private void CurrencyManager_PositionChanged(Object o,
    System.EventArgs ea)
{
    ToolTip1.SetToolTip(txtEmployeeName,
       txtEmployeeName.Tag.ToString());
}

3.
Insert the Main() method to launch the form. Set the form as the startup object for the project.

4.
Run the project. As you scroll through the records, you see that the ToolTip for the employee name TextBox control is kept synchronized by the PositionChanged event code.


If you compare the code in Step by Step 5.9 with the code from Guided Practice Exercise 5.1, you see that using the PositionChanged event can save you from writing duplicate code in every procedure where you might change the Position property of the CurrencyManager class.

Using the Data Form Wizard

Now that you've seen the mechanics of data binding, it's time to explore one of the tools that Visual C# .NET offers for automatic data binding: the Data Form Wizard. In the following sections, you'll see how to use the wizard to build both a single-table form and a multiple-table form. This helps you ease into the broad topic of using data from databases, which occupies the rest of this chapter and all of Chapter 6.

IN THE FIELD: A CRASH COURSE IN DATABASES

Although this exam doesn't have any objectives that explicitly demand database knowledge, you can't pass the exam without knowing something about databases. These days, databases are part of the pervasive understructure of computing. You're expected to understand the basics of them, just as you understand the basics of files and folders.

At this point you're interested in data stored in relational databases. A relational database (such as Microsoft SQL Server, which is used in the examples in this book) stores data in tables, each of which represents an instance of a particular entity. An entity is anything that you're interested in tracking in the database: a customer, an order, an employee, or a supplier, for example. A single database can contain many tables; for example, you might have tables named Customers, Orders, Employees, and Suppliers.

Each table contains one row (or record) for each instance of an entity: If you have 50 customers, then the Customers table should have 50 rows. Each row consists of a number of columns (or fields) that describe the entity. For example, these might be the fields in a Customers table:

  • Customer Number

  • Customer Name

  • City

  • State

In a well-designed database, each entity can be identified by a column or combination of columns called the primary key. For example, the primary key for the Customers table could be the Customer Number column. Each customer would then have a unique and unchanging customer number. If you knew the customer number, you could use it to look up all the other information that the database stores about that customer.

SQL Server is called a relational database because it accommodates the fact that there are relationships between entities that are stored in different tables. Think about customers and orders, for example. Each order is placed by a single customer. You can indicate this by storing the customer number (the primary key of the Customers table) in the Orders table. These might be the columns of the Orders table:

  • Order Number

  • Customer Number

  • Order Date

  • Delivery Date

In this case, the Order Number column would be the primary key (the unique identifying column) of the Orders table. The Customer Number column in the Orders table serves to relate each order to a corresponding row in the Customers table. You call the Customer Number a foreign key in the Orders table. To specify a relationship between tables, you name the two tables and the columns that match between them. Relationships can be one-to-many (one customer can place many orders) or one-to-one (one employee has at most one pension).

Databases can contain other objects in addition to tables. These include views (which can provide a subset of information from one or more tables) and stored procedures (which are groups of SQL statements that are compiled into execution plans) and users and groups (which control the security of database objects).

NOTE

Database Design For more information on relational database design and terminology, refer to Que's SQL Server 2000 Programming by Carlos Rojas and Fernando Guerrero.


If you've never worked with a database, you may find it a bit confusing at first. But if you work through the examples carefully, it should become clear to you.


Building a Single-Table Data Form

You'll start by building a data form that displays data from a single table: the Customers table in the Northwind sample database.

STEP BY STEP

5.10 Building a Single-Table Data Form

1.
Select Project, Add New Item. In the Add New Item dialog box (shown in Figure 5.8), select the Data Form Wizard. Name the new form StepByStep5_10.cs and click Open.

Figure 5.8. The Add New Item dialog box allows you to launch the Data Form Wizard.


2.
Read the Welcome screen of the wizard and click Next.

3.
The next screen helps you choose a DataSet object to use with the data form. A DataSet object is a .NET Framework object that you can think of as representing one or more tables from a database (it's actually more flexible than that, but that's enough for this example). On this screen, shown in Figure 5.9, choose to create a new DataSet object named dsCustomers. Click Next.

Figure 5.9. The Data Form Wizard allows you to create a new DataSet object or use an existing DataSet object.


4.
The next screen helps you choose or build a data connection. A data connection tells Visual C# .NET which database contains the data you want to retrieve. You haven't set up any data connections yet, so click the New Connection button. This opens the Data Link Properties dialog box.

5.
Click the Provider tab of the Data Link Properties dialog box and select Microsoft OLE DB Provider for SQL Server.

EXAM TIP

Specifying Connection Information You might have to try a few things to connect to the database. For the server name, you can use the name of a computer on which SQL Server is running. If SQL Server is installed on the same computer where you're writing the code, you can use the special name (local) instead of entering a server name. For logon information, you should first try Windows NT Integrated Security, which logs on to SQL Server by using your Windows identity. If that fails, try using the specific user name sa and a blank password. If that also fails, you need to check with the person who is responsible for the SQL Server to find out what login information to use. Note the Test Connection button at the bottom of the dialog box; it is handy when you try to get your login information correct.

6.
Click on the Connection tab of the Data Link Properties dialog box and enter the information that you need to use the Northwind database, as shown in Figure 5.10.

Figure 5.10. You can use the Data Form Wizard to create a data connection to the Northwind sample database.


7.
Click OK on the Data Link Properties dialog box to create the connection and return to the Data Form Wizard. Select the new connection in the combo box (it should have a name such as MACHINENAME.Northwind.dbo) and click Next.

WARNING

Blank Passwords You might use blank passwords when developing applications. However, using blank passwords when deploying the final application is dangerous for the real data. You should always use strong passwords for production databases.

8.
On the Choose Tables or Views screen of the wizard, select the Customers table in the Available Items list and click the > button to move the table to the Selected Items list, as shown in Figure 5.11. Click Next.

Figure 5.11. You can choose tables and views as data sources for data binding.


9.
On the Choose Tables and Columns to Display on the Form screen, leave all the columns in the table selected and click Next.

10.
On the Choose the Display Style screen, shown in Figure 5.12, select the single record style and check all the optional check boxes. Click Finish.

Figure 5.12. You can choose a display style and the additional controls on the form through the Data Form Wizard.


11.
Insert the Main() method to launch the form. Set the form as the startup form for the project and run the project to experiment with the data form.


Figure 5.13 shows the finished Data Form created in Step by Step 5.10. It contains 10 buttons, which have the following functions:

  • Load— Load all the data from the database and bind it to the form.

  • Update— Save all changes to the database.

  • Cancel All— Discard all changes without changing the database.

  • <<— Move to the first row.

  • <— Move to the previous row.

  • >— Move to the next row.

  • >>— Move to the last row.

  • Add— Add a new row.

  • Delete— Delete the current row.

  • Cancel— Cancel changes to the current row.

Figure 5.13. The finished data form displays all the data from the Customers table, providing options to delete rows, add rows, and save changes back to the original database.


NOTE

Data Form Wizard–Generated Code May Not Be Optimal The Data Form Wizard is designed to create output for a variety of combinations of forms (Windows forms, Web forms, and so on), controls, and data sources. Therefore, the code generated by the wizard might not be the optimal code for any one specific situation. As you progress through this book, you will learn techniques for optimizing the data access code.


You might want to browse through the code that the wizard created behind this form. Be warned, though, that there are more than 600 lines of code involved in implementing this functionality! Obviously, the Data Form Wizard can save you a lot of time in building data-bound forms.

As you continue through the book, you'll learn more about database objects and the code that you can use to manipulate them. For now, you'll stick to the relatively easy user interface tools as you explore what can be done with data binding.

Building a Multiple-Table Data Form

You can use the Data Form Wizard to build a form that displays data from more than one table. Step by Step 5.11 explains how to do this.

STEP BY STEP

5.11 Building a Multiple-Table Data Form

1.
Select Project, Add New Item. In the Add New Item dialog box, select the Data Form Wizard. Name the new form StepByStep5_11.cs and click Open.

2.
Read the Welcome screen of the wizard and click Next.

3.
On the Choose a Dataset screen, choose to create a new DataSet object named dsCustOrders. Click Next.

4.
On the Choose a Data Connection screen, select the data connection that you created in Step by Step 5.10, and click Next.

NOTE

Relationships Creating a relationship between tables tells the wizard which fields it should treat as primary and foreign keys. Refer to the sidebar “A Crash Course in Databases,” earlier in this chapter if you need to review this concept.

5.
On the Choose Tables or Views screen, select the Customers table in the Available Items list and click the > button to move it to the Selected Items list. Also select the Orders table and click the > button to move it to the Selected Items list. Click Next.

6.
The next screen helps you specify the relationship between the two tables, Customers and Orders. Name the new relationship relCustomerOrders. Select Customers as the parent table and Orders as the child table. Select CustomerID as the key field in each table. Figure 5.14 shows the wizard at this point. Click the > button to create the new relationship, and then click Next.

Figure 5.14. When you use the Data Form Wizard to create a relationship between tables, it automatically takes care of generating code to keep the tables synchronized.


7.
On the Choose Tables and Columns to Display on the Form screen, leave all the columns in both tables selected and click Next.

8.
On the Choose the Display Style screen, select the All Records in a Grid style and check the Cancel All check box. Click Finish.

9.
Insert the Main() method to launch the form. Set the new form as the startup form for the project and run the project to experiment with the data form.


As you select different Customer table rows in the upper DataGrid control on the form created in Step by Step 5.11, the lower DataGrid control changes to show only the Order rows for that customer. Figure 5.15 shows an example.

Figure 5.15. This two-table data form uses one DataGrid control for each table, and it uses code to keep the two DataGrid controls synchronized.


Once again, you'll find a tremendous amount of code (about 500 lines) behind this form. And once again, you'll leave it for future inspection.

REVIEW BREAK

  • In one-way data binding, data from the data model is displayed on the form, but changes to the form do not affect the data model.

  • In two-way data binding, data from the data model is displayed on the form, and changes to the form are also written back to the database.

  • The .NET Framework uses BindingContext and CurrencyManager objects to manage data binding.

  • You can use events of the CurrencyManager object to help react to changes in bound data.

  • The Data Form Wizard helps you quickly create data-bound forms, both simple and complex. These forms draw their data from a relational database such as SQL Server.


..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.222.48.60