Windows Forms Data Binding

One of the most powerful aspects of .NET and Windows Forms is data binding. Historically, data binding was used to bind views to data stored in databases. Some database management systems, such as Microsoft Access, have provided GUI APIs to help developers quickly bind to data.

 

This sample is taken from Chapter 6: "Windows Forms Data Binding" of the Blue Vision Title "Developing .NET Custom Controls and Designers using C#"


PropertyManager

The PropertyManager is used to identify and maintain the current property of an object. PropertyManager derives from BindingManagerBase; but oddly, most of all of the base properties and methods are overridden to do nothing. For instance, setting the Position property of the object has no effect. Also, the AddNew and RemoveAt methods throw a NotSupportedException. Your guess is as good as mine as to why this object was derived from BindingManagerBase. As of this writing, PropertyManager is only used by the PropertyGrid. The PropertyGrid uses the current property to raise events, display property descriptions, and invoke appropriate editors.

The following code shows how to return a PropertyManager from the BindingContext:

Customer singleCustomer = new Customer();

PropertyManager pm = this.BindingContext [singleCustomer] as PropertyManager;

Simple Binding Example

With simple binding, the property  on a control is bound to a single data source member. The data source will typically be a collection, array, or DataTable. If the data source is a collection or array, the binding will occur on the property of a collection item. If the data source is a DataTable, the binding will occur on a DataColumn of the DataTable.

In an example, we will walk through the implementation of binding customer data to controls on a form.

First, create a new Windows Form in Visual Studio .NET. Drag a group box to the form. Then drag three labels and three text boxes to the form. Finally, drag four buttons to the form. Your form should look similar to the following:

Name your controls as follows:

Group Box: _groupBox1

First Name Label :  _firstNameLabel

Last Name Label: _lastNameLabel

Phone Number Label: _phoneNumberLabel

First Name Textbox: _firstNameTextBox

Last Name Textbox: _lastNameTextBox

Phone Number Textbox: _phoneNumberTextBox;

First Button: _firstButton

Previous Button: _previousButton

Next Button: _nextButton

Last Button: _lastButton

For each of the four buttons, add an event handler for the Click event called Button_Navigate. To do this manually, the syntax would be:

_firstButton.Click += new EventHandler(this.Button_Validate);

Now, we must define a Customer. Create a new C# class file, and add the following:

public class Customer

{

private String _ firstName;

private String _lastName;

public String _phoneNumber;

public Customer(String firstName, String lastName, String phoneNumber)

  {

      _firstName = firstName;

      _lastName = lastName;

      _phoneNumber = phoneNumber;

  }

  public String FirstName

  {

     get

      {

            return_firstName;

      }

  }

  public String LastName

  {

     get

      {

           return _lastName;

      }

  }

  public String PhoneNumber

  {

      get

      {

            return _phoneNumber;

      }

  }

}

For simplicity, we are only storing a customer's name and phone number.

Inside the constructor of the Form, we will initialize an array of customers with some arbitrary values. We will declare the array as readonly since we do not intend to reassign the collection value.

privatereadonly Customer[] _customers = null;

public Form1()

{

     InitializeComponent( );

      _customers = new Customer[]

            {

                  new Customer("James", "Henry", "123-123-1234"),

                  new Customer("Bill", "Gates", "234-234-2345"),

                  new Customer("Tupac", "Shakur", "345-345-3456"),

            }

}

We will now data bind all text box controls to the Customer array. This is illustrated as follows:

  private readonly Customer[] _customers = null;

public Form1()

  {

      InitializeComponent();

      _customers = new Customer[]

            {

                 new Customer("James", "Henry", "123-123-1234"),

                 new Customer("Bill", "Gates", "234-234-2345"),

                  new Customer("Tupac", "Shakur", "345-345-3456"),

           }

      _firstNameTextBox.DataBindings.Add("Text", _customers, "FirstName");

      _lastNameTextBox.DataBindings.Add("Text", _customers, "LastName");

      _phoneNumberTextBox.DataBindings.Add("Text", _customers, "PhoneNumber");

  }

Finally, we must handle the Click event of the buttons in order to provide navigation:

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

  {

      BindingManagerBase manager = _groupBox1.BindingContext [_customers];

     if (sender == _firstButton)

      {

            manager.Position = 0;

      }

      else if (sender == _previousButton)

      {

            manager.Position--;

      }

      else if (sender == _nextButton)

      {

            manager.Position++;

      }

      else if (sender == _lastButton)

      {

            manager.Position = manager.Count  1;

      }

  }

The Button_Navigate handler handles the Click event for all four navigation buttons. In this code, we first retrieve the BindingManagerBase object from the BindingContext of _groupBox1. The instance is actually a CurrencyManager . We simply ask the BindingContext to "give me the CurrencyManager for the _customers data source." The CurrencyManger changes its Position property, depending on which button was clicked. As the Position is changed, all bound controls are updated automatically.

Now, in order to demonstrate the purpose of the BindingContext, let's add another group box to the form, and three more text boxes to this group box. Name these controls as follows:

Group Box: _groupBox2

First Name Label: _firstNameLabel2

Last Name Label: _lastNameLabel2

Phone Number Label: _phoneNumberLabel2

First Name Textbox: _firstNameTextBox2

Last Name Textbox: _lastNameTextBox2

Phone Number Textbox: _phoneNumberTextBox2

We will now data bind this second set of text box controls to the Customer array. This is illustrated as follows:

  privatereadonly Customer[] _customers = null;

public Form1()

  {

      InitializeComponent();

      _customers = new Customer[]

           {

                  new Customer("James", "Henry", "123-123-1234"),

                  new Customer("Bill", "Gates", "234-234-2345"),

                  new Customer("Tupac", "Shakur", "345-345-3456"),

            }

      _firstNameTextBox.DataBindings.Add("Text", _customers, "FirstName");

      _lastNameTextBox.DataBindings.Add("Text", _customers, "LastName");

      _phoneNumberTextBox.DataBindings.Add("Text", _customers, "PhoneNumber");

     _firstNameTextBox2.DataBindings.Add("Text", _customers, "FirstName");

      _lastNameTextBox2.DataBindings.Add("Text", _customers, "LastName");

      _phoneNumberTextBox2.DataBindings.Add("Text", _customers, "PhoneNumber");

  }

Before we can actually see the advantage of the BindingContext, we must ensure that each set of text boxes "lives" in its own binding context. To do this, we must create a BindingContext for each group box. Remember that by default, the Form automatically creates a single BindingContext for it and all child controls. Here is the constructor after creating two new BindingContext objects:

  privatereadonly Customer[] _customers = null;

  public Form1()

  {

     InitializeComponent();

      _customers = new Customer[]

            {

                  new Customer("James", "Henry", "123-123-1234"),

                  newCustomer("Bill", "Gates", "234-234-2345"),

                  new Customer("Tupac", "Shakur", "345-345-3456"),

            }

      _groupBox1.BindingContext = new BindingContext();

      _groupBox2.BindingContext = new BindingContext();

      _firstNameTextBox.DataBindings.Add("Text", _customers, "FirstName");

      _lastNameTextBox.DataBindings.Add("Text", _customers, "LastName");

     _phoneNumberTextBox.DataBindings.Add("Text", _customers, "PhoneNumber");

      _firstNameTextBox2.DataBindings.Add("Text", _customers, "FirstName");

      _astNameTextBox2.DataBindings.Add("Text", _customers, "LastName");

      _phoneNumberTextBox2.DataBindings.Add("Text", _customers, "PhoneNumber");

  }

Now, each group box and any child controls have their own context for data binding. Even through the controls in both group boxes may bind to the same data source, they will be bound using different CurrencyManager objects.

We can visualize how the text box controls are data bound and synchronized in the following diagram:

From the diagram above, we can see that each group box has its own CurrencyManager  for the Customers data source. Therefore, changing the Position property on the first CurrencyManager will have no effect on the text boxes contained in the second group box. And changing the Position property on the second CurrencyManager will have no effect on the text boxes contained in the first group box.

Comments

Be the first to write a comment

You must me logged in to write a comment.