Data Binding
Several of the built-in Dynamic Data field templates, including the DateTime and DateTime_Edit templates, rely on data binding to display column value on the page.
<asp:TextBox ID="textBox" runat="server" Text='<%# FieldValueEditString %>' />
In addition, the field template controls themselves are data bound, and several properties of the FieldTemplateUserControl base class rely on the data binding mechanics. These properties can be used safely only from the DataBind method and will throw an InvalidOperationException when used outside of the data binding context, such as during a post-back:
- Row provides access to the entity instance the field template is bound to, such as an Order object. In Read-only and Edit modes, it is an equivalent of calling the Page.GetDataItem method. In Insert mode, this property returns a CustomTypeDescriptor object that provides access to default field values extracted by the page template from the page request URL (more on this in Chapter 6, “Page Templates”).
- FieldValue returns value of the column property, such as Order.OrderDate. It is an equivalent of calling DataBinder.Eval(Row, Column.Name).
- FieldValueString converts the field value to a display string in Read-only mode, using the formatting options specified in the data model or page markup. It is an equivalent of calling FormattingOptions.FormatValue(FieldValue).
- FieldValueEditString converts the field value to a string in Edit or Insert mode, using the formatting options specified in the data model or page markup. It is an equivalent of calling FormattingOptions.FormatEditField(FieldValue).
During a post-back, a field template itself does not actually save the new column value back in its Row object. Instead, the FieldTemplateUserControl class implements the IBindableControl interface and relies on the parent FormView, DetailsView, and GridView controls to perform this task. When the parent control is handling an Insert or an Update command, it iterates through the list of its child controls and calls the ExtractValues of each child that implements IBindableControl interface.
protected override void ExtractValues(IOrderedDictionary dictionary) { dictionary[this.Column.Name] = this.ConvertEditedValue(this.textBox.Text); }
The ExtractValues method receives a dictionary object used to store column name and value pairs. The extract from the DateTime_Edit field template just shown is a typical implementation of this method. The ConvertEditedValue method is provided by the FieldTemplateUserControl base class. It can automatically convert the field value to null based on the ConvertEmptyStringToNull and NullDisplayText formatting options.
Notice that by providing the dictionary argument, ExtractValue method allows a field template to return values of other columns or even multiple column values. This capability is important for the foreign key templates used for navigation properties, such as the Customer property of the Order class in the Northwind data model, that need to return values of the corresponding primitive properties, such as CustomerID. Consider Listing 3.9, which shows the markup of the ForeignKey_Edit template.
Listing 3.9. ForeignKey_Edit Field Template (Markup)
<%@ Control Language="C#" CodeBehind="ForeignKey_Edit.ascx.cs" Inherits="WebApplication.DynamicData.FieldTemplates.ForeignKey_EditField" %> <asp:DropDownList ID="dropDownList" runat="server"/> <asp:RequiredFieldValidator runat="server" ID="requiredFieldValidator" ControlToValidate="dropDownList" Enabled="false" /> <asp:DynamicValidator runat="server" ID="dynamicValidator" ControlToValidate="dropDownList" />
Notice that the ForeignKey_Edit template uses a DropDownList control, which allows users to select from a list of items created programmatically, in the Page_Load method shown in Listing 3.10. Each ListItem object in the DropDownList represents a single foreign key, with Text property storing value of the display column from the referenced table and Value property storing a comma-separated list of its primary key columns. This code relies on the convenience method, called PopulateListControl, provided by the FieldTemplateUserControl base class, which does all the heavy lifting required to query the referenced table, create the ListItem objects, and add them to the DropDownList.
Listing 3.10. ForeignKey_Edit Field Template (Code-Behind)
using System; using System.Collections.Specialized; using System.Web.DynamicData; using System.Web.UI; using System.Web.UI.WebControls; namespace WebApplication.DynamicData.FieldTemplates { public partial class ForeignKey_EditField : FieldTemplateUserControl { public override Control DataControl { get { return this.dropDownList; } } protected void Page_Load(object sender, EventArgs e) { if (this.dropDownList.Items.Count == 0) { if (this.Mode == DataBoundControlMode.Insert || !this.Column.IsRequired) this.dropDownList.Items.Add(new ListItem("[Not Set]", string.Empty)); this.PopulateListControl(this.dropDownList); } this.SetUpValidator(this.requiredFieldValidator); this.SetUpValidator(this.dynamicValidator); } protected override void OnDataBinding(EventArgs e) { base.OnDataBinding(e); string selectedValueString = this.GetSelectedValueString(); if (this.dropDownList.Items.FindByValue(selectedValueString) != null) this.dropDownList.SelectedValue = selectedValueString; } protected override void ExtractValues(IOrderedDictionary dictionary) { this.ExtractForeignKey(dictionary, this.dropDownList.SelectedValue); } } }
To display the current field value, the ForeignKey_Edit template cannot rely on the FieldValueEditString property because the FieldValue in this case consists of one or more values of the underlying primitive columns. For the Customer column of the Order table, this would mean displaying CustomerID, “ALFKI”, instead of the Customer’s CompanyName, “Alfreds Futterkiste”, which is not very user friendly. Instead, the ForeignKey_Edit template overrides the OnDataBinding method to select the matching item in the DropDownList. It calls the GetSelectedValueString, a convenience method provided by the FieldTemplateUserControl base class. For foreign key columns, this method is equivalent to calling ForeignKeyColumn.GetForeignKeyString(Row) and returns a comma-separated list of values of the primitive columns in the foreign key.
The ForeignKey_Edit template also overrides the ExtractValues method and calls the ExtractForeignKey provided by the FieldTemplateUserControl base class. Given a comma-separated list of column values, this method stores them in the dictionary under the names of the foreign key columns. Under the hood, this method simply calls ForeignKeyColumn.ExtractForeignKey(dictionary, selectedValue).