Monday, September 19, 2011

Windows Forms Data Binding - 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, an array, or a DataTable object. If the data source is a collection or an array, binding will occur with a property of an item in the collection or array. If the data source is a DataTable object, binding will occur with a DataColumn object of the DataTable object.

In this example, I will walk through the implementation of binding customer data to controls on a form. First, create a new Windows Application project in Visual Studio .NET 2003. Drag a GroupBox control to the form. Then drag three Label controls and three TextBox controls to the form inside the GroupBox control. Finally, drag four Button controls to the form outside.

the GroupBox control. Arrange and label the controls like the ones shown in the following
figure:


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 named Button_Navigate for the Click event. To dynamically associate the event handler with the events, use the following syntax:

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

To do this declaratively, ensure that the Button controls are declared using the WithEvents keyword, and add a Handles clause that specifies each Button control’s Click event to the declaration of the Button_Navigate event handler, as shown here:

 private void Button_Navigate(object sender, EventArgs e)
        {
        }
Now, you need to define a Customer class. Create a new Visual Basic .NET class file and add the following code to it:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
public class Customer
{
    private string _firstName;
    private string _lastName;
    private 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, this example only stores a customer’s name and phone number. In a production application, more properties would likely need to be implemented.

Inside the constructor of the Form1 class after the call to InitializeComponent, initialize an array of customers with some arbitrary values. Declare the array as Readonly since it will not be reassigned after initialization. Here is the code:

Private ReadOnly Customer[] _customers;
   
    public 
DummyClass() {
        
// This call is required by the Windows Form Designer.
        
InitializeComponent();
        
// Add any initialization after the InitializeComponent() call
        
_customers = new Customer[] {
                
new Customer("James", "Henry", "123-123-1234"),
                
new Customer("Bill", "Gates", "234-234-2345"),
                
new Customer("Tupac", "Shakur", "345-345-3456")};
    
}


Now bind all TextBox controls to the array, as shown here:

_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, you need to handle the Click event raised by the Button controls to provide navigation functionality, as shown here:

private void Button_Navigate(object sender, EventArgs e) {
        BindingManagerBase manager 
= _groupBox1.BindingContext(_customers);
        if 
((sender == _firstButton)) {
            manager.Position 
= 0;
        
}
        
else if ((sender == _previousButton)) {
            manager.Position 
= (manager.Position - 1);
        
}
        
else if ((sender == _nextButton)) {
            manager.Position 
= (manager.Position + 1);
        
}
        
else if ((sender == _lastButton)) {
            manager.Position 
= (manager.Count - 1);
        
}
    }


The Button_Navigate event handler handles the Click event for all four navigation buttons. This code first retrieves the BindingManagerBase object from the BindingContext object of _groupBox1. The  BindingManagerBase object is actually an instance of CurrencyManager. The code basically asks the BindingContext object to “give me the CurrencyManager for the _customers data source.” The CurrencyManger object changes its Position property when a button is clicked. As the Position property is changed, all bound controls are updated automatically.

Now, to understand the purpose of the BindingContext object, add another GroupBox control to the form and three more TextBox controls to this GroupBox control. 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
First Button: _firstButton2
Previous Button: _previousButton2
Next Button: _nextButton2
Last Button: _lastButton2

Attach the
Click event of each Button control to the same Button_Navigate event handler as before. Now data bind the second set of TextBox controls to the array, as shown here:

_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 you can actually see the advantage of the BindingContext object, you need to ensure that each set of TextBox controls “lives” in its own data binding context. To do this, you need to create a BindingContext object for each GroupBox control. Remember that by default, the form automatically creates a single BindingContext object for itself and all child controls. Here is the constructor after creating two new BindingContext objects:

_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");


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

You can visualize how the TextBox controls are data bound and synchronized in the following diagram:


From the previous diagram, you can see that each GroupBox control has its own
CurrencyManager object for the Customers data source. Therefore, changing the Position property on the first CurrencyManager object will have no effect on the TextBox controls contained in the second GroupBox control. Similarly, changing the Position property on the second CurrencyManager object will have no effect on the TextBox controls contained in the first GroupBox control. Therefore, add the following code to the Button_Navigate event handler to manipulate the second CurrencyManager object:

private void Button_Navigate(object sender, EventArgs e) {
        BindingManagerBase manager 
= _groupBox1.BindingContext(_customers);
        if 
((sender == _firstButton)) {
            manager.Position 
= 0;
        
}
        
else if ((sender == _previousButton)) {
            manager.Position 
= (manager.Position - 1);
        
}
        
else if ((sender == _nextButton)) {
            manager.Position 
= (manager.Position + 1);
        
}
        
else if ((sender == _lastButton)) {
            manager.Position 
= (manager.Count - 1);
        
}
    }


Next :Windows Forms Data Binding - Data Binding Interfaces

Windows Forms Data Binding - Data Consumers and Binding Controls


Data Consumers
A data consumer is an object or component that uses the data exposed by a data provider with the intent to display or modify that data. In Windows Forms, a data consumer is typically a data-bound control. Simple data-bound controls include, but are not limited to, TextBox, Label, CheckBox, and RadioButton controls. These controls can display only one data value provided by a data source. On the other hand, the DataGrid, ListBox, and ComboBox controls can display a list of values. These controls are therefore referred to as complex data-bound controls.

Binding and BindingContext
Windows Forms controls support either simple binding or complex binding. Controls that support simple binding include the TextBox control. A TextBox control can support only one data value at a time. The following example shows how to bind a TextBox control with the FirstName column of a Customers DataTable object:

        TextBox nameTextBox = new TextBox();
        DataSet dataSet = CreateMyDataSet();   
        nameTextBox.DataBindings.Add("Text", dataSet, "Customers.FirstName");
 
Binding
Every Windows Form control has a DataBindings property, which is an instance of ControlBindingsCollection. The ControlBindingsCollection class represents a collection of Binding objects that bind the properties of controls to data source members. 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 can be bound to different data sources.

BindingContext
Every container control on a form, including the form itself, contains at least one BindingContext object. Actually, all controls derived from System.Windows.Forms.Control have the BindingContext property, but only container controls really make use of it. Noncontainer controls simply return the BindingContext object of their immediate container. A BindingContext object 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 object enables retrieval of any particular data source. Specifically, a BindingContext object manages a collection of BindingManagerBase objects. BindingManagerBase is an abstract class thatenables synchronization of data-bound controls that are bound to the same data source. A BindingContext object can be visualized as follows (The dashed lines represent the BindingContext object):Form

GroupBox



In the previous figures, the BindingContext object basically 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 object 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 object:

BindingManagerBase customers = this.BindingContext(DataSet, "Customers");
Here is the syntax for creating a new BindingContext object:

groupBox1.BindingContext = new BindingContext();
groupBox2.BindingContext = new BindingContext();

In the previous example, two BindingContext objects are created and are assigned to two GroupBox controls. This allows the contained controls in both GroupBox controls to be bound to the same data source, but using two different binding managers.

CurrencyManager
Any data source that is bound to a Windows Forms control is associated with a CurrencyManager object. Actually, the true name for CurrencyManager should be “concurrency manager” or “current manager.” During the days of ActiveX Data Objects (ADO), the data source object itself kept track of the current record. The problem with this approach was that multiple data consumers could not reuse the same data source object concurrently in an efficient manner. For example, if there were two Grid controls on a form
that used ADO to display their data, and if both Grid controls used the current record for highlighting purposes, there would have been no way for each Grid control to highlight a different item at the same time. With .NET, the current record is no longer maintained in the data source object itself, which makes the data source object truly disconnected from the actual data store. The current record is maintained by a CurrencyManager object. A CurrencyManager object has a one-to-one relationship with a data source.
A CurrencyManager object 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 object per data source per BindingContext object.) The following diagram shows the relationship between a Form control, a Panel control,CurrencyManager objects, and data sources:


In the previous diagram, the Form control contains the automatically created
BindingContext object, which contains two CurrencyManager objects, one managing an array and the other managing a collection. The Panel control contains a newly created BindingContext object (remember that only the Form control’s BindingContext object is created by default), which also contains two CurrencyManager objects, one managing the same collection that is bound to the Form control and the other managing a DataTable object. Under normal circumstances, only one CurrencyManager object 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:

In the previous diagram, a particular control has three properties that are participating in data binding, as you can note from the three Binding objects. These bindings are stored in the control’s ControlBindings property, which is an instance of ControlBindingsCollection.

Remember, the
ControlBindingsCollection class is a collection of Binding objects, and a Binding object 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 object ensures that the properties that are associated with the same data source are synchronized

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

            Customer singleCustomer = new Customer();
            PropertyManager pm = this.BindingContext(singleCustomer);


Next: Simple Binding Example

Windows Forms Data Binding

Overview
One of the most powerful aspects of the Windows Forms architecture is data binding.
Historically, data binding was used to bind visual views to data stored in databases and other
data sources. Some database management systems (DBMSs), such as Microsoft Access, have
provided GUI APIs to help developers quickly bind data to visual controls. Each DBMS
usually had its own 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, was 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, database tables, rows, and 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 world. A
consumer is an object or component that uses the data exposed by a provider with the intent to
display or modify that data. With .NET data binding, the minimum requirement to support listbased
data binding is for the provider to implement the IList interface. The IList interface
represents an index-based collection. Data providers and data consumers are discussed in more
detail in the following sections.

Data Providers
The following objects implement the IList interface, so they are inherently data providers.

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 class is a .NET representation of a database or of some other data source. 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 object, the data consumer is responsible for asking for the particular
DataTable or DataView object or objects with which data binding should occur. In some
cases, the data consumer would really be binding to the DataSet object’s default
DataViewManager instance.

DataTable
A DataTable object typically represents a table in a database. However, it may also be used to
represent the structure of an XML element or the parent of a collection. A DataTable object
contains collections of DataColumn objects and DataRow objects. Complex controls, such as
the DataGrid control, can be bound to a DataTable object with ease. Note that when you bind
to a DataTable object, you really bind to the table’s default DataView object.

DataView
A DataView object is simply a customized view of the data in a DataTable object. For
example, it might contain all rows sorted by a particular column or all rows that match a
certain filter expression. When data binding to a DataView object, all controls involved in the
data binding process will receive a snapshot of the data at that particular moment during which
data binding occurs. 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 class represents the default settings of an entire DataSet object.
Similar to the DataView class, it is a snapshot view of the DataSet object. The main difference
is that the DataViewManager class also includes relations.

DataColumn
A DataColumn object typically represents, and is analogous to, a column in a database table.
However, it may also represent an XML attribute or an attribute-less XML element. You can
only simple-bind to a DataColumn object. This means that only simple controls, such as a
TextBox control, can be bound to a DataColumn object.

Other .NET Objects
In actuality, any .NET object can support data binding, but you might not automatically reap
all of the benefits provided by the .NET architecture for using just any object. Also, when
binding to these objects, only the public properties (not public fields) can be bound. 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 created by Visual Studio .NET 2008.
Be careful. You can only bind to the public properties, not the public fields,
of data source objects.

Next -
Data Consumers and Binding and BindingContext

Query Dataset using LINQ


LINQ to DataSet makes it easier and faster to the query on the data cached in a DataSet. Specifically, LINQ to DataSet simplifies the query to allow developers to write queries in the programming language itself, rather than using a separate query language. This is especially useful for Visual Studio developers who can now take advantage of syntax checking at compile time, static typing, and IntelliSense support provided by Visual Studio in their offices.

LINQ to DataSet can also be used to query about the data that has been consolidated in one or more data sources. This enables many scenarios that require flexibility in the amount of data is represented and handled as consulting locally aggregated data caching and mid-level Web applications. In particular, generic reporting, analysis and business intelligence applications require this method of handling.

The LINQ to DataSet functionality is exposed primarily through extension methods and classes in DataRowExtensions DataTableExtensions. LINQ to DataSet is based and uses the existing ADO.NET 2.0 architecture and is not intended to replace ADO.NET 2.0 in the application code. Effective ADO.NET 2.0 code will still work on an application of LINQ to DataSet. The relationship of LINQ to DataSet in ADO.NET 2.0 and the data store is illustrated in the diagram below.
LINQ to DataSet is based on the ADO.NET provider.

Querying DataSets

Formulating queries with LINQ to DataSet is similar to using Language-Integrated Query (LINQ) against other LINQ-enabled data sources. Remember, however, that when you use LINQ queries over a DataSet object you are querying an enumeration of DataRow objects, instead of an enumeration of a custom type. This means that you can use any of the members of the DataRow class in your LINQ queries. This lets you to create rich, complex queries.

As with other implementations of LINQ, you can create LINQ to DataSet queries in two different forms: query expression syntax and method-based query syntax. For more information about these two forms, see Getting Started with LINQ on MSDN. You can use query expression syntax or method-based query syntax to perform queries against single tables in a DataSet, against multiple tables in a DataSet, or against tables in a typed DataSet.

Single-Table Queries

 Language-Integrated Query (LINQ) queries work on data sources that implement the IEnumerable<T> interface or the IQueryable interface. The DataTable class does not implement either interface, so you must call the AsEnumerable method if you want to use the DataTable as a source in the From clause of a LINQ query.
The following example gets all the online orders from the SalesOrderHeader table and outputs the order ID, order date, and order number to the console.
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable orders = ds.Tables["SalesOrderHeader"];

var query =
    from order in orders.AsEnumerable()
    where order.Field<bool>("OnlineOrderFlag") == true
    select new
    {
        SalesOrderID = order.Field<int>("SalesOrderID"),
        OrderDate = order.Field<DateTime>("OrderDate"),
        SalesOrderNumber = order.Field<string>("SalesOrderNumber")
    };

foreach (var onlineOrder in query)
{
    Console.WriteLine("Order ID: {0} Order date: {1:d} Order number: {2}",
        onlineOrder.SalesOrderID,
        onlineOrder.OrderDate,
        onlineOrder.SalesOrderNumber);
}
The local variable query is initialized with a query expression, which operates on one or more information sources by applying one or more query operators from either the standard query operators or, in the case of LINQ to DataSet, operators specific to the DataSet class. The query expression in the previous example uses two of the standard query operators: Where and Select.
The Where clause filters the sequence based on a condition, in this case that the OnlineOrderFlag is set to true. The Select operator allocates and returns an enumerable object that captures the arguments passed to the operator. In this above example, an anonymous type is created with three properties: SalesOrderID, OrderDate, and SalesOrderNumber. The values of these three properties are set to the values of the SalesOrderID, OrderDate, and SalesOrderNumber columns from the SalesOrderHeader table.

Cross-Table Queries
This is done by using a join. A join is the association of objects in one data source with objects that share a common attribute in another data source, such as a product or contact ID. In object-oriented programming, relationships between objects are relatively easy to navigate because each object has a member that references another object. In external database tables, however, navigating relationships is not as straightforward. Database tables do not contain built-in relationships. In these cases, the join operation can be used to match elements from each source. For example, given two tables that contain product information and sales information, you could use a join operation to match sales information and products for the same sales order.
The Language-Integrated Query (LINQ) framework provides two join operators, Join 
and GroupJoin. These operators perform equi-joins: that is, joins that match two 
data sources only when their keys are equal. (By contrast, Transact-SQL supports 
join operators other than equals, such as the less than operator.) 



// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);
 
DataTable orders = ds.Tables["SalesOrderHeader"];
DataTable details = ds.Tables["SalesOrderDetail"];
 
var query =
    from order in orders.AsEnumerable()
    join detail in details.AsEnumerable()
    on order.Field<int>("SalesOrderID") equals
        detail.Field<int>("SalesOrderID")
    where order.Field<bool>("OnlineOrderFlag") == true
    && order.Field<DateTime>("OrderDate").Month == 8
    select new
    {
        SalesOrderID =
            order.Field<int>("SalesOrderID"),
        SalesOrderDetailID =
            detail.Field<int>("SalesOrderDetailID"),
        OrderDate =
            order.Field<DateTime>("OrderDate"),
        ProductID =
            detail.Field<int>("ProductID")
    };
 
 
foreach (var order in query)
{
    Console.WriteLine("{0}\t{1}\t{2:d}\t{3}",
        order.SalesOrderID,
        order.SalesOrderDetailID,
        order.OrderDate,
        order.ProductID);
}
I found great resources about this topic on MSDN here : LINQ to DataSet