12.1. Overview of Data Binding

 < Day Day Up > 

Data binding provides a way to link the contents of a control with an underlying data source. The advantage to this linkage or "binding" is that changes to the immediate data source can be reflected automatically in data controls bound to it, and changes in the data control are posted automatically to the intermediate data source. The term intermediate data source is used to distinguish it from the original data source, which may be an external database. The controls cannot be bound directly to a data source over an active connection. Binding is restricted to the in-memory representation of the data. Figure 12-1 shows the basic components of the binding model: the original data source, the intermediate storage, and the Form controls that are bound to values in the local storage through a binding object. Let's examine the model in more detail.

Figure 12-1. Multiple controls bound to a single data source

Simple Data Binding

Simple data binding, which is available to all controls, links a data source to one or more properties of a control. A good example is the Textbox control that exposes easily recognizable properties such as Text, Width, and BackColor. An application can set these dynamically by binding them to a data source. Here is a code segment that creates an object whose public properties are mapped to the properties on the TextBox.

 // Create object (width, text, color) TextParms tp = new TextParms(200, "Casablanca", Color.Beige); // Bind text and BackColor properties of control txtMovie.DataBindings.Add("Text", tp, "Tb_Text"); txtMovie.DataBindings.Add("BackColor", tp, "Tb_Background"); // Or create binding and then add in two steps Binding binding = new Binding("Width", tp, "Tb_Width"); txtMovie.DataBindings.Add(binding); 

The DataBindings.Add method creates a collection of bindings that links the data source to the control's properties. The method's syntax is

 DataBindings.Add( control property, data source, data member) 

control property

Property on the control that is being bound.

data source

Object that contains data being bound to control.

data member

Data member on the data source that is being used. Set this to null if the data source's ToString() method provides the value.

A control may have multiple bindings associated with it, but only one per property. This means that the code used to create a binding can be executed only once; a second attempt would generate an exception. To avoid this, each call to add a binding should be preceded with code that checks to see if a binding already exists; if there is a binding, it should be removed.

 if (txtMovie.DataBindings["Text"] != null)      txtMovie.DataBindings.Remove(txtMovie.DataBindings["Text"]); txtMovie.DataBindings.Add("Text", tp, "Tb_Text"); 

Binding to a List

The true value of data binding becomes obvious when the data source contains multiple items to be displayed. In the preceding example, the control was bound to a single object. Let's now create an array of these objects each representing a different movie. Instead of binding to a single object, the control is bound to the array (see Figure 12-2). The control can still only display a single movie title at a time, but we can scroll through the array and display a different title that corresponds to the current array item selected. This scrolling is accomplished using a binding manager, which is discussed shortly.

Figure 12-2. Binding TextBox properties to objects in a list

This example creates an ArrayList of objects that are used to set the TextBox properties on the fly.

 ArrayList tbList = new ArrayList(); // Beige color indicated movie won oscar as best picture tbList.Add(new TextParms(200,"Casablanca",Color.Beige)); tbList.Add(new TextParms(200, "Citizen Kane", Color.White)); tbList.Add(new TextParms(200, "King Kong", Color.White)); // Bind to properties on the Textbox txtMovie.DataBindings.Add("Text", tbList, "Tb_Text"); txtMovie.DataBindings.Add("BackColor", tbList,                            "Tb_Background"); txtMovie.DataBindings.Add("Width", tbList, "Tb_Width"); 

The one difference in the bindings from the preceding example is that the data source now refers to the ArrayList. By default, the TextBox takes the values associated with the first item in the array. When the index of the array points to the second row, the displayed value changes to "Citizen Kane".

Simple Binding with ADO.NET

Binding to a table in a DataSet is basically the same as binding to a list. In this example, the Text property of the control is bound to the movie_Year column in a DataTable.

 ds = new DataSet("films"); string sql = "select * from movies order by movie_Year"; da = new SqlDataAdapter(sql, conn); da.Fill(ds,"movies");      // create datatable "movies" // Bind text property to movie_Year column in movies table txtYr.DataBindings.Add("Text", ds,"movies.movie_Year"); 

Although the control could be bound directly to a DataTable, the recommended approach is to bind the property to a DataSet and use the DataTable name as a qualifier to specify the column that provides the data. This makes it clear which table the value is coming from.

Complex Data Binding with List Controls

Complex binding is only available on controls that include properties to specify a data source and data members on the data source. This select group of controls is limited to the ListBox, CheckedListBox, ComboBox, DataGrid, and DataGridView. Complex binding allows each control to bind to a collection of data the data source must support the IList interface and display multiple items at once. Because the DataGridView is discussed at length in the last half of this chapter, let's look at how complex binding is implemented on the ListBox control. The details also apply to other List controls.

Binding a list control to a data source requires setting a minimum of two properties: DataSource, which specifies the source, and DisplayMember, which describes the member usually a data column or property in the data source that is displayed in the control. This code segment illustrates how a ListBox bound to a DataSet displays movie titles:

 da.Fill(ds,"movies"); DataTable dt = ds.Tables[0];  // Minimum properties to bind listbox to a DataTable listBox1.DataSource = ds; listBox1.DisplayMember = "movies.movie_Title"; // Optional property that assigns a value to each item row listBox1.ValueMember = "movies.movie_ID"; 

After these values are set, the list box is automatically filled. The DataSource property can be changed programmatically to fill the control with a different set of data, or it can be set to null to clear the control's content. Note also that although no Binding object is explicitly created, a DataBindings collection is created underneath and is accessible through code.

The bound list box control is often grouped with other controls, such as a text box or label, in order to display multiple values from a row of data. When the controls are bound to the same data source, scrolling through the list box causes each control to display a value from the same data row. To illustrate, let's add the following simple bindings to the preceding code:

 txtStudio.DataBindings.Add("Text", ds,"movies.studio"); txtYear.DataBindings.Add("Text", ds,"movies.movie_Year"); 

These text boxes display the studio name and year of the movie currently selected in the list box (see Figure 12-3).

Figure 12-3. Using data binding to populate controls on a form

One-Way and Two-Way Data Binding

The data bound to a control can be changed in two ways: by updating the underlying data source, such as adding a row to a table, or by modifying the visible contents of the control. In both cases, the changes should be reflected in the associated control or data source a process referred to as two-way data binding. In general, that is what happens. However, a control may be bound to a data source in read-only mode when its only purpose is to present data. To understand how these techniques are implemented, let's look at how updating occurs from the perspective of the control and the data source.

Effects on the Data Source of Updating a Control Value

By default, changes made to data in a control are also made to the underlying in-memory data source. If the year value in Figure 12-3 is changed, the value in the corresponding row and column of the DataTable is also changed. Note that if the year is represented as an integer in the table, the value entered in the control must be an integer value. Data binding automatically checks types and rejects values (keeps the same value in the control) that do not match the type of the underlying data.

In the case where a control is bound to a property on an object, the property must provide write support in order for its value to be updated. For example, if the year and studio list boxes in the preceding example were bound to the following properties, respectively, only year could be updated; changes made to the studio control would be ignored and it would revert to its original value.

 public int Movie_Year { set { myYear = value;  }                         get { return myYear;   } } // Read only property. Control cannot update this. public string Studio  { get { return myStudio; } } 

Note that changes in a control are not propagated to the data source until the user moves to another item in the GUI control. Underneath, this changes the current position within the binding manager firing an event that causes the data to be updated.

Effects on a Control of Updating the Data Source

When a DataSet is used as the data source for controls, any additions, deletions, or changes made to the data are automatically reflected in the associated bound control(s). Custom data sources require some programming assistance to accomplish this.

If a control is bound to an object property, a change to the value of that property is not automatically sent to the control. Instead, the binding manager looks for an event named propertyChanged on the data source. If found, it provides a handler for this event to receive notice when that property's value changes. To enable the binding manager to handle a changed value, you must define a propertyChanged event on the data source class, and fire the event when a change occurs. To illustrate, let's extend the previous example to add the event to the class containing the Movie_Year property, and add code to fire the event when the property changes.

 // Event to notify bound control that value has changed public event EventHandler Movie_YearChanged; // Property control is bound to year value public int Movie_Year {    set {           myYear = value;           // Notify bound control(s) of change           if (Movie_YearChanged != null)               Movie_YearChanged(this, EventArgs.Empty);    }    get { return myYear; } } 

The other situation to handle is when a data item is deleted from or added to the data source. Controls that are bound to the source using simple binding are updated automatically; controls using complex binding are not. In the latter case, the update can be forced by executing the Refresh method of a CurrencyManager object. As we see next, the CurrencyManager is a binding manager especially designed for list data sources.

Using Binding Managers

As illustrated in Figure 12-4, each data source has a binding manager that keeps track of all connections to it. When the data source is updated, the binding manager is responsible for synchronizing the values in all controls bound to the data. Conversely, if a value is changed on one of the bound controls, the manager updates the source data accordingly. A binding manager is associated with only one data source. Thus, if an application has controls bound to multiple data sources, each will have its own manager.

Figure 12-4. Binding managers synchronize the data source and controls

Binding requires the interaction of several objects to coordinate the two-way flow of data between a data source and control. Let's look at the four most important objects, which are denoted by numbers in Figure 12-4.

  1. Binding. Maintains a simple binding between a property on a control and a property on a single object. The following statements demonstrate how to create a binding and gain access to it:

     txtYr.DataBindings.Add("Text", ds,                         "movies.movie_Year"); Binding binding = txtYr.DataBindings["Text"]; // "txtYr" MessageBox.Show(binding.Control.ToString());  // Create a binding manager object BindingManagerBase mgr= binding.BindingManagerBase; 

  2. CurrencyManager. This class derives from the abstract BindingManagerBase class and serves as a binding manager for list data sources such as a DataTable or Array. This object provides five members that an application can use to manage the relationship between a data source and control:

    • Bindings. Returns the collection of bindings being managed.

    • Count. The number of rows in the list that is being managed.

    • Current. Returns the current item (such as a row) in the data source as an object.

    • Position. Gets/sets the position in the data source currently indexed by the control.

    • PositionChanged. Fires when the Position in the list changes.

    • CurrentChanged. Is triggered when the bound value changes.

  3. PropertyManager. This class, which also derives from BindingManagerBase, maps the properties on an object to properties on a bound control.

  4. BindingContext. Observe in Figure 12-4 that the BindingContext is linked to a form and a collection of BindingManagerBase objects. Its job is to manage a collection of binding managers for a specific control in this case, a Form. The control could just as well be a Panel or GroupBox on a form. A program's main interest in the BindingContext is to use it to gain access to the binding manager for a data source. These statements, for example, return the manager for the table movies.

     BindingManagerBase mgr = this.BindingContext[ds,"movies"]; // Or use casting to get specific manager. CurrencyManager mgr= (CurrencyManager)                       this.BindingContext[ds,"movies"]; 

Using the BindingManagerBase to Navigate a List

Let's now look at how the members of the BindingManagerBase class are used to move through the items in a source data list and simultaneously update the contents of controls bound to the list. The example binds a list box and text box to the familiar movies data table.

 // Bind listbox to a dataset.datatable listBox1.DataSource = ds; listBox1.DisplayMember = "movies.movie_Title"; // Bind to TextBox txtStudio.DataBindings.Add("text", ds, "movies.studio"); // BindingManagerBase bmb has class-wide scope  bmb = this.BindingContext[ds, "movies"]; // Create delegate pointing to event handler  bmb.PositionChanged += new        EventHandler(bmb_PositionChanged); 

The following method moves to the next item in the data source list when a button is clicked. It would typically be paired with a method to move backward in the list.

 // This method moves to the next row in the table.  // If at the end of the table it moves to the beginning. private void Forward_Click (object sender, EventArgs e) {    if (listBox1.Items.Count > 0)    {       bmb.Position = bmb.Position >= bmb.Count - 1 ? 0 :               ++bmb.Position;    } } 

The PositionChanged event is fired each time the binding manager moves to a new position in the list. This could be triggered programmatically or by the user clicking a row in the list box control.

 private void bmb_PositionChanged(object sender,                                  EventArgs e) {    BindingManagerBase bmb = (BindingManagerBase)sender;    // Item should be a DataRowView if from a table    object ob = bmb.Current.GetType();    if (ob == typeof(System.Data.DataRowView))    {       DataRowView view = (DataRowView)bmb.Current;       // Could access: ((string)view["movie_Title"]);    } } 

Note that the Current property is used to return an object representing the current item. The data source in this example is a data table, but the object returned is not the expected DataRow it is a DataRowView object. It is up to the code to provide the proper casting to access properties in the selected item.

     < Day Day Up > 

    Core C# and  .NET
    Core C# and .NET
    ISBN: 131472275
    EAN: N/A
    Year: 2005
    Pages: 219

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net