2. Data-Binding Properties
In ASP.NET, there are two main categories
of data-bound controls—list and iterative controls. As we’ll see in
detail later on, list controls repeat a fixed template for each item
found in the data source. Iterative controls are more flexible and let
you define the template to repeat explicitly, as well as other
templates that directly influence the final layout of the control.
All data-bound controls implement the DataSource and DataSourceID properties, plus a few more, as detailed in Figure 1.
Note that in ASP.NET 1.x, there’s no DataSourceID property. Likewise, no intermediate classes such as BaseDataBoundControl and DataBoundControl exist in versions of ASP.NET prior to 2.0. ListControl and BaseDataList form the common base for list and iterative controls.
Note
For some reason, the Repeater control—a low-level iterative control—doesn’t inherit from either of the classes in the diagram. It inherits directly from the Control class. |
The DataSource Property
The DataSource
property lets you specify the data source object the control is linked
to. Note that this link is logical and does not result in any overhead
or underlying operation until you explicitly order to bind the data to
the control. As mentioned, you activate data binding on a control by
calling the DataBind method. When the
method executes, the control actually loads data from the associated
data source, evaluates the data-bound properties (if any), and
generates the markup to reflect changes.
public virtual object DataSource {get; set;}
The DataSource property is declared of type object and can ultimately accept objects that implement either IEnumerable (including data readers) or IListSource. By the way, only DataSet and DataTable implement the IListSource interface.
The DataSource
property of a data-bound control is generally set programmatically.
However, nothing prevents you from adopting a kind of declarative
approach as follows:
<asp:DropDownList runat="server" id="theList"
DataSource="<%# GetData() %>"
...
/>
In this example, GetData is a public or protected member of the code-behind page class that returns a bindable object.
Note
How can a data-bound control figure out which object it is actually bound to? Is it a collection, a data reader, or perhaps a DataTable? All standard data-bound controls are designed to work only through the IEnumerable interface. For this reason, any object bound to DataSource is normalized to an object that implements IEnumerable. In some cases, the normalization is as easy (and fast) as casting the object to the IEnumerable interface. In other cases—specifically, when DataTable and DataSet
are involved—an extra step is performed to locate a particular named
collection of data that corresponds to the value assigned to the DataMember
property. There’s no public function to do all this work, although a
similar helper class exists in the ASP.NET framework but is flagged as
internal. What this helper class does, though, can be easily replicated
by custom code: it just combines an array of if statements to check types and does casting and conversion as appropriate. |
The DataSourceID Property
Introduced with ASP.NET 2.0, the DataSourceID
property gets or sets the ID of the data source component from which
the data-bound control retrieves its data. This property is the point
of contact between data-bound controls and the new family of data
source controls that includes SqlDataSource and ObjectDataSource.
public virtual string DataSourceID {get; set;}
By setting DataSourceID,
you tell the control to turn to the associated data source control for
any needs regarding data—retrieval, paging, sorting, counting, or
updating.
Like DataSource, DataSourceID
is available on all data-bound controls. The two properties are
mutually exclusive. If both are set, you get an invalid operation
exception at run time. Note, though, that you also get an exception if DataSourceID is set to a string that doesn’t correspond to an existing data source control.
The DataMember Property
The DataMember property gets or sets the name of the data collection to extract when data binding to a data source:
public virtual string DataMember {get; set;}
You use the property to specify the name of the DataTable to use when the DataSource property is bound to a DataSet object:
DataSet data = new DataSet();
SqlDataAdapter adapter = new SqlDataAdapter(cmdText, connString);
adapter.Fill(data);
// Table is the default name of the first table in a
// DataSet filled by an adapter
grid.DataMember = "Table";
grid.DataSource = data;
grid.DataBind();
DataMember and DataSource can be set in any order, provided that both are set before DataBind is invoked. DataMember has no relevance if you bind to data using DataSourceID with standard data source components.
Note
This
is not a limitation of the binding technology, but rather a limitation
of standard data source components, which don’t support multiple views.
We’ll return to this point later when discussing data source components. |
The DataTextField Property
Typically used by list controls, the DataTextField property specifies which property of a data-bound item should be used to define the display text of the nth element in a list control:
public virtual string DataTextField {get; set;}
For example, for a drop-down
list control the property feeds the displayed text of each item in the
list. The following code creates the control shown in Figure 2:
CountryList.DataSource = data;
CountryList.DataTextField = "country";
CountryList.DataBind();
The same happens for ListBox, CheckBoxList, and other list controls. Unlike DataMember, the DataTextField property is necessary also in case the binding is operated by data source components.
Note
List controls can automatically format the content of the field bound through the DataTextField property. The format expression is indicated via the DataTextFormatString property. |
The DataValueField Property
Similar to DataTextField, the DataValueField property specifies which property of a data-bound item should be used to identify the nth element in a list control:
public virtual string DataValueField {get; set;}
To understand the role of
this property, consider the markup generated for a drop-down list, set
as in the code snippet shown previously:
<select name="CountryList" id="CountryList">
<option selected="selected" value="[All]">[All]</option>
<option value="Argentina">Argentina</option>
<option value="Austria">Austria</option>
...
</select>
The text of each <option> tag is determined by the field specified through DataTextField; the value of the value attribute is determined by DataValueField. Consider the following code that fills a ListBox with customer names:
CustomerList.DataMember = "Table";
CustomerList.DataTextField = "companyname";
CustomerList.DataValueField = "customerid";
CustomerList.DataSource = data;
CustomerList.DataBind();
If DataValueField is left blank, the value of the DataTextField property is used instead. Here’s the corresponding markup generated by the preceding code snippet:
<select size="4" name="CustomerList" id="CustomerList">
<option value="BOTTM">Bottom-Dollar Markets</option>
<option value="LAUGB">Laughing Bacchus Wine Cellars</option>
...
</select>
As you can see, the value attribute now is set to the customer ID—the unique, invisible value determined by the customerid field. The content of the value attribute for the currently selected item is returned by the SelectedValue property of the list control. If you want to access programmatically the displayed text of the current selection, use the SelectedItem.Text expression.
The AppendDataBoundItems Property
Introduced
in ASP.NET 2.0, this Boolean property indicates whether the data-bound
items should be appended to the existing contents of the control or
whether they should overwrite them. By default, AppendDataBoundItems is set to false,
meaning that data-bound contents replace any existing contents. This
behavior is the same as you have in ASP.NET 1.x, where this property
doesn’t exist.
public virtual bool AppendDataBoundItems {get; set;}
AppendDataBoundItems
is useful when you need to combine constant items with data-bound
items. For example, imagine you need to fill a drop-down list with all
the distinct countries in which you have a customer. The user selects a
country and sees the list of customers who live there. To let users see
all the customers in any country, you add an unbound element, such as [All]:
<asp:DropDownList runat="server" ID="CountryList"
AppendDataBoundItems="true">
<asp:ListItem Text="[All]" />
</asp:DropDownList>
With AppendDataBoundItems set to false (which is the default behavior in ASP.NET 1.x), the [All]
item will be cleared before data-bound items are added. In ASP.NET 1.x,
you need to add it programmatically after the binding operation
completes.
Note
Using
additional items not generated by the data-binding process can pose
some issues when you consider that users might repeatedly click the
button that fills a given user-interface element. In Figure 2,
a user who clicks the Get Countries button twice will double the
country names in the control. To avoid that, you typically clear the Items collection of the DropDownList
control before rebinding. In doing so, though, items added outside of
data binding will also be removed. Do not forget to add them back or
provide some code that configures the content of the data-bound control
before display. |
The DataKeyField Property
The DataKeyField
property gets or sets the key field in the specified data source. The
property serves the needs of grid-like controls and lets them
(uniquely) identify a particular record. Note that the identification
of the record is univocal only if the field is unique-constrained in
the original data source.
public virtual string DataKeyField {get; set;}
The DataKeyField property is coupled with the DataKeys array property. When DataKeyField is set, DataKeys
contains the value of the specified key field for all the control’s
data items currently displayed in the page.