0% found this document useful (0 votes)
53 views

Windows Form

This document discusses data binding in Windows Forms applications. It explains that data binding allows views to be bound to various data sources, including arrays, datasets, datatables and other objects. It describes the key concepts of data providers, which expose data, and data consumers, which are typically controls that display or modify bound data. It provides examples of binding a textbox to a field in a datatable. It also explains the role of BindingContext and CurrencyManager objects in managing the relationships between controls and their data sources.

Uploaded by

Jurandi Uchoa
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
53 views

Windows Form

This document discusses data binding in Windows Forms applications. It explains that data binding allows views to be bound to various data sources, including arrays, datasets, datatables and other objects. It describes the key concepts of data providers, which expose data, and data consumers, which are typically controls that display or modify bound data. It provides examples of binding a textbox to a field in a datatable. It also explains the role of BindingContext and CurrencyManager objects in managing the relationships between controls and their data sources.

Uploaded by

Jurandi Uchoa
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 30

6

Chapter

Windows Forms Data


Binding

Overview

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. Each DBMS normally had its on associated API for data binding purposes. Some even
had no associated API, which forced developers to provide the implementation from scratch.
Binding to other types of data structures, such as arrays, were out of the question. .NET,
however, solves all these problems and more. With .NET, a client can bind to almost any data
structure, including arrays, collections, data rows, and data views.

Data Binding Concepts

For .NET data binding to be possible, there must be providers of data and consumers of data.
A provider of data is an object or component that exposes its data to the outside. A consumer is
an object or component that uses the data exposes by a provider with the intention to display or
modify that data. With .NET data binding, the minimum requirement to support list-based data
binding is for the provider to implement the IList interface. The IList interface represents an
index-based collection.

Data Providers

The following objects implement the IList interface; so they are, inherently, data providers.
C H A P T E R 6

Arrays

An array is simply a collection of objects that can be accessed by a numeric index. Arrays can
be either single-dimensional or multi-dimensional.

DataSet

The DataSet is a .NET representation of a database. It does not, however, need to actually be
connected to a real database. As a matter of fact, it acts as a “disconnected” data source, with
the ability to track changes and merge new data. When binding to a DataSet, the consumer is
responsible for asking for the particular DataTable to bind to. In some cases, the consumer
would really be binding to the DataSet’s default DataViewManager.

DataTable

A DataTable typically represents a table in a database. Though, it may also be used to


represent the structure of an XML element, or the parent of a collection. A DataTable contains
a DataColumn collection and a DataRow collection. Complex controls, such as the .NET
DataGrid, can be bound to a DataTable with ease. Note that when you bind to a DataTable,
you really bind to the table’s default DataView.

DataView

A DataView is simply a customized view of the data in a DataTable. For instance, it may
contain all rows sorted by a particular column, or all rows that match a certain expression.
When data binding to a DataView, all controls involved in the binding process will receive a
snapshot of the data at that particular moment. Whenever the underlying data changes, the
bound controls must have some way of knowing how to refresh themselves. This process will
be discussed shortly.

DataViewManager

The DataViewManager represents the entire DataSet. Like the DataView, it is a customized
snapshot view of the DataSet. The only difference is that it also includes relations.

DataColumn

A DataColumn typically represents, and is analogous to, a column in a database table.


Although, it may also represent an XML attribute or an attributeless XML element. You can
only simple-bind to a DataColumn. This means that only simple controls, such as a text box,
can be bound to a DataColumn.

Other .NET Objects

112
W I N D O W S F O R M S D A T A B I N D I N G

In actuality, any .NET object may support data binding, but you may not automatically reap all
of the benefits provided by the .NET architecture. Also, when binding to these objects, only
the public properties (not public fields) can be bound to. Therefore, you must be careful when
data binding to data sources exposed by web services. The public properties of any types
returned by a web service will be converted to public fields in the web service’s client proxy
code.

Be careful. You can only bind to the public properties, not the public fields,
of data source objects.

Data Consumers

A consumer is an object or component that uses the data exposed by a provider whose intent is
to display or modify that data. In Windows Forms, a consumer is typically a data-bound
control. Simple data-bound controls include, but are not limited to, text boxes, labels, check
boxes, and radio buttons. These controls can only display one data value provided by a data
source. On the other hand, controls such as data grids, list boxes and combo boxes can display
a list of values. These controls are therefore referred to as complex data-bound controls.

Binding and BindingContext

.NET Controls either support simple binding or complex binding. Controls that support simple
binding include the text box. A text box can only support one data value at a time. The
following example shows how to bind a text box control with a name field of a Customers
DataTable:

TextBox nameTextBox = new TextBox();


DataSet dataSet = CreateMyDataSet();

nameTextBox.DataBindings.Add(“Text”, dataSet,
“Customers.FirstName”);

113
C H A P T E R 6

Binding

Every Windows Form control has a DataBindings property, which is an instance of


ControlBindingsCollection. The ControlBindingsCollection is a collection of Binding objects,
which bind the property of a control to a data source member. Whenever the data source
member changes, the control’s property is automatically updated to reflect the change, and
vice versa.

Different properties of the same control may also be bound to different data sources.

BindingContext

Every container control on a Windows Form, including a form itself, contains at least one
BindingContext. Actually, all controls derived from System.Windows.Forms.Control have the
BindingContext property, but only container controls really make use of it. Non-container
controls will simply return the BindingContext of their immediate container. A
BindingContext is just an object that provides binding support to multiple data sources. Since
more than one data source can be viewed on a form, the BindingContext enables retrieval of
any particular data source. Specifically, a BindingContext manages a collection of
BindingManagerBase objects. BindingManagerBase is an abstract class that enables
synchronization of data-bound controls that are bound to the same data source. A
BindingContext can be visualized as follows (The dashed lines represent the BindingContext):

Form

MyForm

Panel

GroupBox

114
W I N D O W S F O R M S D A T A B I N D I N G

GroupBox

In the pictures above, the BindingContext simply says, “I will manage and keep track of all
controls and their associated data sources and data-bound members. If the current record in the
one of the managed data sources changes, I will refresh all controls that I track with the new
values.”

By default, only one BindingContext is created for a Form, regardless of the number of
controls contained on the form.

Here is the syntax for retrieving a data source from the BindingContext:

BindingManagerBase customers = this.BindingContext[dataSet,


“Customers”];

Here is the syntax for creating a new BindingContext.

groupBox1.BindingContext = new BindingContext();


groupBox2.BindingContext = new BindingContext();

In the snippet above, two BindingContext objects are created and are assigned to two group
box controls. This allows the contained controls in both group boxes to be bound to the same
data source, but using two different binding managers.

The two classes derived from BindingManagerBase are described next.

115
C H A P T E R 6

CurrencyManager

Any data source that is bound to a .NET Windows Forms control will be associated with a
CurrencyManager. Actually, the true name for CurrencyManager should be “concurrency
manager” or “current manager.” During the days of ADO, the collection itself kept track of the
current record. The problem with this approach was that multiple consumers could not reuse
the same collection concurrently in an efficient manner. For example, if there were two grid
controls on a dialog that used ADO to display their data, and if both grids used the current
record for highlighting purposes, there would be no way for each grid to highlight a different
item at the same time. With .NET, the current record is not maintained in the data source itself,
which makes the data source truly disconnected. The current record is, instead, maintained by
the CurrencyManager. A CurrencyManager has a one-to-one relationship with a data source.

A CurrencyManager is automatically created when a Binding object is created, if it is the first


time that the data source has been bound. ( Remember that there is only one CurrencyManager
per data source per BindingContext.)

The following diagram shows the relationship between a Form, Panel, CurrencyManager
objects, and data sources:

116
W I N D O W S F O R M S D A T A B I N D I N G

MyForm

BindingContext CurrencyManager
Panel

DataTable

BindingContext CurrencyManager

CurrencyManager Collection

Array

In the diagram above, the Form contains the automatically created BindingContext, which
contains two CurrencyManager objects, one managing an array, and the other managing a
collection. The Panel contains a newly created BindingContext (remember that only the
Form’s BindingContext is created by default), which also contains two CurrencyManager
objects, one managing the same collection that is bound to the Form, and the other managing a
DataTable. Normally, only one CurrencyManager would be created for the Collection; but
since there are two BindingContext objects, each must contain its own collection of
CurrencyManager objects.

The following diagram shows control binding in action:

117
C H A P T E R 6

Control

ControlBindingsCollection

Binding Binding Binding

Property Property Property

DataSource DataSource DataSource

DataMember DataMember DataMember

BindingContext

CurrencyManager CurrencyManager

DataSource DataSource

In the diagram above, a particular control has three properties that are participating in data
binding, as we can note from the three Binding objects. These bindings are stored in the
control’s ControlBindings property, which is an instance of ControlBindingsCollection. The
ControlBindingsCollection class is a collection of Binding objects. A Binding associates the
property of a control with a data source member. Whenever the data source member value
changes, the control’s property is updated, and vice-versa. Two of the bindings are associated
with the same data source, while the third one is associated with a different data source. The
CurrencyManager ensures that the properties that are associated with the same data source are
synchronized.

118
W I N D O W S F O R M S D A T A B I N D I N G

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:

119
C H A P T E R 6

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

120
W I N D O W S F O R M S D A T A B I N D I N G

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

121
C H A P T E R 6

{
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.

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”),
}
}

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”),

122
W I N D O W S F O R M S D A T A B I N D I N G

_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.

123
C H A P T E R 6

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:

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”);

_firstNameTextBox2.DataBindings.Add(“Text”, _customers,
“FirstName”);
_lastNameTextBox2.DataBindings.Add(“Text”, _customers,
“LastName”);

124
W I N D O W S F O R M S D A T A B I N D I N G

_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:

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”),
}

_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”);
_lastNameTextBox2.DataBindings.Add(“Text”, _customers,
“LastName”);
_phoneNumberTextBox2.DataBindings.Add(“Text”, _customers,
“PhoneNumber”);

125
C H A P T E R 6

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:

BindingContext for _groupBox1

First Name:

Last Name:

Phone Number:

BindingContext for _groupBox2

First Name:

Last Name:

Phone Number:

CurrencyManager
Customers
Data Source
CurrencyManager

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.

126
W I N D O W S F O R M S D A T A B I N D I N G

And changing the Position property on the second CurrencyManager will have no effect on the
text boxes contained in the first group box.

Data Binding Interfaces

.NET provides a standard set of interfaces related to data binding. Each of these interfaces is
described below.

IList

Any class that implements IList must support a list of homogenous types. That is, all list items
must be of the same type. The first item in the list always determines the type. Some of the
base classes that implement IList include Array, ArrayList, CollectionBase, DataView, and
DataViewManager.

Typed IList

Similar to IList, the list must be of homogenous types. However, classes of this type can only
be data bound at runtime.

IList and IComponent

When a class implements both IList and IComponent, the class can be data bound at design
time. When dragging the component from the toolbox to a form, you will notice that the
component appears in the component tray, like a DataSet and DataAdapter.

IListSource

This interface allows an object to “act” like a list for data binding purposes. The implemented
object is not an instance of IList, but it should be able to provide one. The DataSet and
DataTable objects both implement this interface. IListSource provides a single property and a
single method which are described below:

127
C H A P T E R 6

ContainsListCollection: Indicates whether the collection is a collection of IList


objects. For the DataSet implementation, this property returns true, because the
DataSet contains a collection of collections. For the DataTable implementation,
this property returns false, because the DataTable contains a collection of objects.
In simple terms, implement this property to indicate how deep to go for returning
a bindable list.

GetList: Returns the IList that will be data-bound. The DataSet uses this property to
return a DataViewManager. The DataTable uses this property to return a
DataView.

ITypedList

This interface allows an object to expose its items’ properties. This interface is useful in
situations where the public properties of the object should be different than the properties
available for data binding. This interface is also necessary during complex binding when a list
is empty but you still need to know the properties of the list item. (Remember, IList alone uses
the data type of the first item in the list.) This is useful when columns headers should be
created for empty lists.

IBindingList

This interface offers change notification when the list and list items have changed. There is one
property, SupportsChangeNotification, which determines whether this interface’s ListChanged
event will be raised. The ListChangedEventArgs contains a ListChangedType property for
describing the type of change that occurred. The available ListChangedType values are as
follows:

ItemAdded: An item has been added to the list. The index of the new item is the value
of ListChangedEventArgs.NewIndex.

ItemChanged: An item in the list has been changed. The index of the changed item is
the value of ListChangedEventArgs.NewIndex.

ItemDeleted: An item has been removed from the list. The index of the deleted item is
the value of ListChangedEventArgs.NewIndex.

128
W I N D O W S F O R M S D A T A B I N D I N G

ItemMoved: An item has been moved to another location within the list. The previous
index is the value of ListChangedEventArgs.OldIndex. The new index is the
value of ListChangedEventArgs.NewIndex.

PropertyDescriptorAdded: A PropertyDescriptor has been added.

PropertyDescriptorChanged: A PropertyDescriptor has been changed.

PropertyDescriptorDeleted: A PropertyDescriptor has been deleted.

Reset: The list has a lot of changes and controls should refresh themselves.

IEditableObject

This interface supports transaction-like operations. It allows objects to specify when changes
should be made permanent. Hence, it allows changes to be rolled back. The DataGrid is one
control that opts to call methods of this interface. The following methods are defined in this
interface:

BeginEdit: Signals that an edit operation has started. Any changes to the object should
be temporarily stored after this method has been called. When implementing this
method, be sure that back-to-back calls are non-destructive. That is, the method
itself should not create any changes to any temporary objects.

CancelEdit: Cancels any changes made after the BeginEdit call. In other words, all
temporary objects can be destroyed when this method is called.

EndEdit: Commits any changes made after the BeginEdit call. Once this method is
called, changes cannot and should not be rolled back.

The following example illustrates IEditableObject with an implantation of a Customer object.

public class Customer : IEditableObject


{
private bool _transactionStarted = false;
private String _firstName, _originalFirstName;
private String _lastName, _originalLastName;
private String _phoneNumber, _originalPhoneNumber;

public String FirstName

129
C H A P T E R 6

{
get
{
return _firstName;
}
set
{
_firstName = value;
}
}

public String LastName


{
get
{
return _lastName;
}
set
{
_lastName = value;
}
}

public String PhoneNumber


{
get
{
return _phoneNumber;
}
set
{
_phoneNumber = value;
}
}

void IEditableObject.BeginEdit()
{
if (!_transactionStarted)
{
_transactionStarted = true;

_originalFirstName = _firstName;
_originalLastName = _lastName;
_originalPhoneNumber = _phoneNumber;
}
}

130
W I N D O W S F O R M S D A T A B I N D I N G

void IEditableObject.CancelEdit()
{
if (_transactionStarted)
{
_transactionStarted = false;
_firstName = _originalFirstName;
_lastName = _originalLastName;
_phoneNumber = _originalPhoneNumber;
}
}

void IEditableObject.EndEdit()
{
if (_transactionStarted)
{
_transactionStarted = false;
_originalFirstName = “”;
_originalLastName = “”;
_originalPhoneNumber = “”;
}
}
}

IDataErrorInfo

This interface offers custom error information that controls can bind to. During data binding,
this allows controls to retrieve specific error information from the data source itself. For
instance, if a particular column in a DataTable is an Integer type, setting a field to a string for
this column will cause the data source to return an appropriate error. This interface provides to
properties:

Error: Returns an error message indicating what is wrong.

Item: An indexer that gets the error message for the specified column name or
property name.

131
C H A P T E R 6

Complex Binding Example

In the last section, we saw how to implement simple binding. We discussed how to bind public
properties of controls to properties of objects and columns of DataTable objects, and
synchronize the data, one item at a time. But there are also situations where an entire collection
of data needs to be bound, such as viewing a list of software bugs. Typical controls that
support such complex data binding include the DataGrid, ListBox, ComboBox, and
ErrorProvider controls.

All complex data bound controls will expose two important properties: DataSource and
DataMember. The DataSource property can be any type derived from the interfaces discussed
earlier. The DataMember is a string containing either the table name or a public property to
bind to. For example, if the DataSource is a DataSet, the DataMember should specify which
table to bind to; if the DataSource is a collection, the DataMember should be null; and if the
DataSource is an object that has the binding collection as one of its public properties, the
DataMember will be the name of that property.

In this example, we will utilize the DataGrid to bind to the array of customers used in the
previous simple binding example.

First, drag the DataGrid control from the toolbox to the form that you created in the simple
binding example. Also, drag another set of navigation buttons to the form. Each set of
navigation buttons should correspond to a group box, and hence, a BindingContext. The
DataGrid will display the entire list of customers (which is only three items for this example).
We also want to use the row navigation events of the grid to change the current item in the first
group box. You will have to rearrange the controls and resize the form as shown:

132
W I N D O W S F O R M S D A T A B I N D I N G

Also, in order to tackle another bird with this stone, add a PropertyGrid control to the form.
The PropertyGrid is not added to the toolbox by default, so right-click the toolbox, click
“Customize Toolbox…,” navigate to the .NET Framework Components tab, and select the
PropertyGrid control. The PropertyGrid will be synchronized with the current item in the list,
displaying that item’s properties. The PropertyGrid control should be placed on the form as
shown:

133
C H A P T E R 6

Name your controls as follows:

DataGrid: _dataGrid

PropertyGrid: _propertyGrid

Now, using the code from the simple binding example, add two these lines to the constructor:

_dataGrid1.DataSource = _customers;
_propertyGrid1.DataBindings.Add(“SelectedObject”,
_groupBox1.BindingContext[_customers], “Current”);

Here is a breakdown of what is happening with this code. First, we set the DataSource property
of the data grid to the Customers collection. Since this is the collection we want to bind to,
there is no need to set the DataMember property. Next, we synchronize the PropertyGrid with
the current customer of the first group box. The PropertyGrid exposes a property,
SelectedObject, which is used to display all public browsable properties of an object.

134
W I N D O W S F O R M S D A T A B I N D I N G

Now compile and run the sample. Notice that by clicking the navigation buttons of the first
group box, the PropertyGrid automatically updates its display for the new current object. It
does this with only one line of code. But there is one small problem: Selecting different rows
of the DataGrid does not cause navigation in the first group box that we expected. By now, you
should already know what the problem is. It’s the BindingContext. Since we did not explicitly
assign a BindingContext to the DataGrid, it will use the Form’s default BindingContext. And
in this example, the Form’s default BindingContext isn’t managing any data bindings. To get
around this problem, we simply assign the BindingContext of _groupBox1 to the
BindingContext of the DataGrid, as shown:

_groupBox1.BindingContext = new BindingContext();


_groupBox2.BindingContext = new BindingContext();

_dataGrid1.DataSource = _customers;
_dataGrid1.BindingContext = _groupBox1.BindingContext;

Now if we run the code, navigation will work as expected, with only one extra line of code.
The full constructor is shown below:

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”, “777-777-7777”),
}

_groupBox1.BindingContext = new BindingContext();


_groupBox2.BindingContext = new BindingContext();
_dataGrid1.DataSource = _customers;
_dataGrid1.BindingContext = _groupBox1.BindingContext;
_propertyGrid1.DataBindings.Add(“SelectedObject”,
_groupBox1.BindingContext[_customers], “Current”);

_firstNameTextBox.DataBindings.Add(“Text”, _customers,
“FirstName”);
_lastNameTextBox.DataBindings.Add(“Text”, _customers,
“LastName”);
135
C H A P T E R 6

_phoneNumberTextBox.DataBindings.Add(“Text”, _customers,
“PhoneNumber”);

_firstNameTextBox2.DataBindings.Add(“Text”, _customers,
“FirstName”);
_lastNameTextBox2.DataBindings.Add(“Text”, _customers,
“LastName”);
_phoneNumberTextBox2.DataBindings.Add(“Text”, _customers,
“PhoneNumber”);

Advanced Data Binding

As we know from the previous sections, all controls on a form will contain a DataBindings
property. Here is a view of the PropertyGrid for a TextBox, showing this property:

By default, the Text and Tag property are shown when expanding the DataBindings property.
The Tag property of a control is used to provide custom data associated with the control. You
may add additional properties to this expanded list by choosing them from the Advanced Data

136
W I N D O W S F O R M S D A T A B I N D I N G

Bindings dialog box, which is accessed by clicking the ellipsis next to “Advanced.” This
dialog is shown here:

In order for Advanced Data Binding to work, your form must contain a design-time data
source component. You may provide a design-time data source by dragging a DataSet from
the toolbox.

Once you have a data source component, you simply associate each control you want bound in
the “Advanced Data Binding” dialog with the data source component. As you associate each
property with the data source, the property will be added along with the Text and Tag property
beneath (DataBindings) in the PropertyGrid.

When using Advanced Data Binding, you must be sure that properties are not bound twice. If
you use the Advanced Data Binding dialog to bind a control’s property, and then use the
control’s DataBindings property programmatically to bind the same property, a runtime error
will occur.

137
C H A P T E R 6

Dynamic Properties

By default, any properties set on a control in the designer are persisted either in code or in a
resource file. If the Localization property of the parent form is set to true, a control’s properties
are persisted in a resource file. Otherwise, they are persisted in code.

There may be situations, however, where certain properties should be customized by the user
or some other customization application. These scenarios include customizing the BackColor
of a form, or the FlatStyle property of a button. In situations like these, it is common to
implement configuration files. And with .NET, this capability is built in.

Every Windows Forms application will expect to read from configuration files using a
predetermined naming convention. The format is MyApp.exe.config. For example, if your
application is named MyApp.exe, then your configuration file should be named
MyApp.exe.config, and it should be placed in the same directory as the application itself. Note
that you should not add the MyApp.exe.config file to the project, because it may be
regenerated. If you need a configuration file to use during development, then the file should be
app.config. Set the Build Action for this file to None. Compilation uses this file to regenerate
MyApp.exe.config in the appropriate runtime directory.

Every Windows Forms control has a design-time property named “DynamicProperties.” This
property is a collection of name-value pairs which map key names to property names. These
key names are stored in the application’s configuration file. Property values can then be
persisted in this configuration file and retrieved during a form’s initialization. In code, each
property will be associated with a key name. This key name is then used to read the property’s
value. Here are snapshots of the DynamicProperties section in the PropertyGrid and the
DynamicProperties dialog for the TextBox.Text property:

138
W I N D O W S F O R M S D A T A B I N D I N G

As more properties are selected, which is indicated by a check box, these properties will be
expanded beneath DynamicProperties in the PropertyGrid. The Key mapping on the right
contains a list of all keys that have been added, and which can be cast to that property’s type.
In other words, you can specify more than one property to use the same key, but the value of
that key must be able to be cast to the types of both properties; otherwise it won’t appear in the
list. This is determined by the first property that is configured. For example, the ReadOnly
property will have an available key mapping of all Boolean properties that have been
configured; the Text property will contain a key mapping of all Boolean properties and all
String properties that have been configured.

139
C H A P T E R 6

Summary

In this chapter we looked at the concepts and the architecture behind .NET data binding. We
showed the relationship between controls and the BindingContext, and covered the interfaces
related to data binding. We also walked through examples of the two types of data binding:
simple binding and complex binding.

Simple binding involves binding a single property of a control to a single column or property.
Complex binding involves viewing a collection of objects and properties or rows and columns.
Most importantly, we learned that the synchronization of a data source with its controls is
loosely coupled through the CurrencyManager. Data no longer remembers its current position.
This allows for multiple bindings and synchronization on the same data.

Lastly, we discussed dynamic properties, their use, and how they are persisted in application
configuration files. This concept allows the user to be in more control over the user interface, if
desired.

You should now have a better understanding of data binding in .NET Windows Forms. And
remember, data no longer remembers its current position.

140

You might also like