The BindingSource component provides a layer of indirection
between the underlying data source and the bound UI controls. The usage
pattern for BindingSource is for it to be bound to the data source and for the
UI controls to be bound to the BindingSource. By sitting between the data
source and controls, the BindingSource can provide services on behalf of the
data source. The most important services provided by the BindingSource are:
IBindingList services for non-IBindingLists including
IEnumerable binding
Windows Forms complex data binding in VS 2005 will work
correctly against lists of type IEnumerable when bound through a BindingSource
(V1.0 and V1.1 required IList). The BindingSource up-converts all non
IBindingList based data sources to IBindingLists. In the case of IEnumerable,
the BindingSource will copy all data source elements into an internal list and
indirectly binding controls to the internal list.
_bs = new BindingSource();
// Open SQLConnection
conn.Open();
// Get Reader
SqlDataReader reader = cmd.ExecuteReader();
// Binding IEnumerable
_bs.DataSource = reader;
// Close connection
conn.Close();
// Close connection
this.businessDataGridView.DataSource = _bs;
The IBindingList interface's AddNew() method is called by
Controls to add new items to the bound list. There may be times, such as when
binding to a list of factory objects, where the list may not be able to create
a new instance for the list. In these cases, you can use the extensible AddNew
capabilities of the BindingSource to provide your own implementation for
AddNew().
public void Initialize()
{
_bs = new BindingSource();
// Add Customers
_bs.Add(new Customer("555"));
// Binding Simple Control
this.lastNameTextBox.DataBindings.Add("Text", _bs, "LastName", true);
// Binding Simple Control
_bs.AddingNew += new AddingNewEventHandler(BindingSource_AddingNewEventHandler);
// Bind to the BindingSource (supported at design time)
this.customersDataGridView.DataSource = _bs;
}
private void BindingSource_AddingNewEventHandler(object sender, AddingNewEventArgs e)
{
// Set new item
e.NewObject = _service.CreateNewCustomer();
}
Supports type based binding (required for binding to
factory based types)
The Windows Forms V1.0 and V1.0 designer required an
instance of a type to exist at design time in order to setup design time data
binding. The BindingSource provides type binding services such that it can
"project" a type to bound controls as an empty list of that type. This enables
both design time and runtime binding to factory based business objects:
public void Initialize()
{
// Design time setup
_bs = new BindingSource();
// Design time (or run-time binding to type)
// Controls bound to the BindingSource will act as if they are bound to an empty
// typed list of Customers
_dc.DataSource = typeof(Customer);
}
Provides centralized control for binding operations
A common binding request is the ability to suspend and
resume binding for a data source. In V1 and V1.1 the CurrencyManager provided
SuspendBinding() and ResumeBinding() methods but these only worked for Simple
Binding. When binding through a BindingSource, you can suspend both simple
and complex binding by having the BindingSource disable firing of ListChanged
events (ListChanged events control binding). To do this, set the BindingSource
"RaiseListChangedEvents" property to false.
Simplifies currency management
The BindingSource component exposes most of the
CurrencyManager events and properties (see the Currency Management section for
more information on CurrencyManagers). This enables design time event wire-up
of common currency related events such as "CurrentChanged" and
"PositionChanged".
Simplifies binding to web services via indirection
In V1 and V1.1, when binding to objects such as a DataSet,
customers bind directly to the object (as is expected):
// DataSet is a member variable (_ds)
Services.BooksService service = new Services.BookService();
_ds = service.GetBooksByAuthor(this.AuthorTextBox.Text);
// Bind Grid (show returned results)
this.authorsDataGridView.DataSource = _ds;
// Show details in simple controls
this.priceLabel.DataBindings.Add("Text", _ds, "Price");
this.publishDateLabel.DataBindings.Add("Text", _ds, "PublishDate");
this.ISBNLabel.DataBindings.Add("Text", _ds, "ISBN");
This
works well for simple cases but can be problematic if the source object
changes. As a first attempt, many users try to re-set the bound object (see
below).
// First attempt, reset the DataSet
_ds = service.GetBooksByAuthor(this.AuthorTextBox.Text);
Given this doesn't work, users move on re-setting all bindings:
public void AuthorSelected()
{
DataSet ds = service.GetBooksByAuthor(this.AuthorTextBox.Text);
// Reset All Bindings
ResetBindings(ds);
}
public void ResetBindings(DataSet ds)
{
// Bind Grid (show returned results)
this.authorsDataGridView.DataSource = ds;
// Show details in simple controls
this.priceLabel.DataBindings.Add("Text", ds, "Price");
this.publishDateLabel.DataBindings.Add("Text", ds, "PublishDate");
this.ISBNLabel.DataBindings.Add("Text", ds, "ISBN");
}
While this does work, it requires
more work on the user's part and cannot be setup at design time. For DataSets,
users can short circuit this, but few customers discover this:
// First attempt, reset the DataSet
DataSet ds = service.GetBooksByAuthor(this.AuthorTextBox.Text);
_ds.Clear();
_ds.Merge(ds);
The BindingSource provides a level
of indirection at both design-time and run-time that alleviates the need for
users to reset their bindings or merge data sets.
private BindingSource _bs;
private void Initialize()
{
_bs = new BindingSource();
// Bind to the BindingSource (supported at design time)
this.authorsDataGridView.DataSource = _bs;
// Bind to the BindingSource (supported at design time)
this.priceLabel.DataBindings.Add("Text", _bs, "Price");
this.publishDateLabel.DataBindings.Add("Text", _bs, "PublishDate");
this.ISBNLabel.DataBindings.Add("Text", _bs, "ISBN");
}
public void AuthorSelected()
{
// Reset the BindingSource data source (resets bindings)
_dc.DataSource = service.GetBooksByAuthor(this.AuthorTextBox.Text);
}