From the Source
LANGUAGES: VB.NET
ASP.NET VERSIONS: 3.5
More on Dynamic Data
Building Field Template Controls
By Marcin Dobosz and Scott Hunter
ASP.NET Dynamic Data is a new feature that ships with the .NET Framework version 3.5 Service Pack 1 (SP1). Dynamic Data allows you to rapidly build a data-driven Web site with full support for create-read-update-delete (CRUD) operations. It also supports data validation based on the database schema, which requires you to write only minimal code. Dynamic Data works on top of the LINQ-to-SQL and ADO.NET Entity Framework object-relational mapping (ORM) technologies, and it can be extended to work with other frameworks. It relies on templates, conventions, and metadata to deliver highly functional, yet easily customizable Web sites.
We first wrote about Dynamic Data in An Introduction to Dynamic Data. In this follow-up article, we provide information about how to build field templates to customize the UI for displaying and editing data for specific data types. Our example shows how to use the SliderExtender control from the ASP.NET AJAX Control Toolkit in a field template. (For more information about the AJAX Control Toolkit, and to download it, see the ASP.NET Control Toolkit page on the ASP.NET Web site at http://www.asp.net/ajax/ajaxcontroltoolkit/.)
Introduction to Field Templates
Data-bound controls such as the GridView and DetailsView controls can automatically generate UI for displaying and editing data, based on the type of data to which they are bound. (This is the behavior that you get when you set the AutoGenerateColumns or AutoGenerateRows properties to true.) However, this is an all-or-nothing option if you want to modify the UI or behavior of even a single column (for example, by adding styles or validators), you must declare data control fields for all columns. Even then, the automatically generated controls do not offer any built-in validation support. Thus, using the GridView and DetailsView controls to write a data-bound page with customized display or edit behavior requires you to define individual columns or rows that include ASP.NET controls, then to add hard-coded validation rules and error messages. This results in large and hard-to-maintain pages that often have significant amounts of duplicated or very similar code. It also makes it difficult to separate the data model (the business layer) from the presentation layer.
Dynamic Data solves these problems by introducing field templates. A field template is a user control that is responsible for rendering a single data-bound field. As with user controls in general, field templates can contain any number of controls to manage UI, such as TextBox, DropDownList, and RangeValidator controls. Dynamic Data project templates provide a number of field templates out of the box that create UI for displaying and editing common data types, such as Boolean, integer, and text, as well as foreign-key relation columns. Each field template can have up to three variants, which are used depending on the mode that the data control is in: read-only, edit, or insert. The variants of the field templates are identified by a naming convention. The table in Figure 1 describes each variant.
Field Template Name Pattern |
Mode |
Behavior |
FieldTemplate.ascx |
Read-only |
Displays the field in non-editing scenarios. |
FieldTemplate_Edit.ascx |
Edit |
Displays the field for editing scenarios. This template should contain validation controls. |
FieldTemplate_Insert.ascx |
Insert |
Optional variant of the field template for insert scenarios. The template should contain validation controls. If this variant is not found, the edit-mode variant will be used. |
Figure 1: Variants of the field templates.
At run time, Dynamic Data picks the field template for a given column type and given mode from the available field templates. For example, a string column will be displayed using the Text.ascx template in display (read-only) mode and the Text_Edit.ascx template in edit mode. If a particular variant of a field template does not exist in the project, Dynamic Data will try to choose the most appropriate fallback. For example, the default Dynamic Data project template contains an Integer_Edit.ascx field template, but it does not contain an Integer.ascx template. Therefore, Dynamic Data falls back to the Text.ascx template to render UI for integers in read-only mode.
By default, field templates are located in the \DynamicData\FieldTemplates directory of a Dynamic Data project. As the page developer, you can modify the default templates to customize how data types are represented in the UI. You also can create your own field templates, then provide hints that tell Dynamic Data how to map a data field in the data model to your field template. In addition, the logic for how Dynamic Data maps data fields to field templates is fully customizable.
DynamicField and DynamicControl Classes
You can use field templates in the auto-generate mode of properly configured GridView and DetailsView controls. To do this, place a DynamicDataManager control (a new control that ships as part of Dynamic Data) on the page and call its RegisterControl method in the page s Init event. This configures the GridView or DetailsView to work with Dynamic Data. Figure 2 shows the markup for a DynamicDataManager control and a GridView control. Figure 3 shows the Page_Init handler where the GridView control is registered with the DynamicDataManager control.
Figure 2: Markup for DynamicDataManager and GridView controls.
Protected Sub Page_Init(ByVal sender As Object, ByVal e As EventArgs)
DynamicDataManager1.RegisterControl(GridView1)
End Sub
Figure 3: The Page_Init handler where the GridView control is registered with the DynamicDataManager control.
In situations where you need to specify the precise ordering or layout of the data-bound control s fields, you can use a new type of data control field named DynamicField. The DynamicField acts as a host that automatically chooses the right field template control for rendering a field. The new DynamicControl control behaves the same way as the DynamicField class, but DynamicControl can be used in data controls that use templates to define UI. This includes templates in the FormView and ListView controls, and TemplateField instances in GridView or DetailsView controls.
Building a Custom Field Template
The example in this section illustrates how to build a custom field template for editing integer values. It uses the SliderExtender control from the ASP.NET AJAX Control Toolkit to provide a custom UI for setting the integer value. When a page in a Dynamic Data Web site uses this field template, the result might look something like Figure 4.
Figure 4: A custom field template.
To be able to use the AJAX Control Toolkit, you must add
the AjaxControlToolkit.dll to the project. For a Web Site project, you can create
a Bin directory and copy the file into it. For a Web Application project, use
the Add Reference dialog box to add the DLL. You ll also need to modify the
web.config file by adding the tag-mapping section in the
assembly="AjaxControlToolkit" tagPrefix="ajaxToolkit"/> Figure 5: Add
tag-mapping. To build a new field template, right-click the
\DynamicData\FieldTemplates\ directory, then click Add New Item. Select Dynamic
Data Field, then in the Name box, enter IntegerSlider.ascx. Click Add. This
creates the IntegerSlider.ascx and IntegerSlider_Edit.ascx field templates, as
well as the associated code-behind files. These files contain default versions
of the read-only and edit field templates, along with simple display and edit
logic. They are very similar to the Text.ascx and Text_Edit.ascx field
templates. For this example, you need only a custom edit field template (that
is, you do not need a read-only version of the template). Therefore, delete the
IntegerSlider.ascx file from the project. Next, open the IntegerSlider_Edit.ascx and
IntegerSlider_Edit.ascx.vb files and replace the default code with the code
shown in Figures 6 and 7, respectively. <%@ Control Language="C#"
AutoEventWireup="true" CodeFile="IntegerSlider_Edit.ascx.vb" Inherits="IntegerSlider_EditField" %> Text='<%#
FieldValueEditString %>' TargetControlID="TextBox1" BoundControlID="Label1" /> ControlToValidate="TextBox1" Display="Dynamic" /> Figure 6: Open the
IntegerSlider_Edit.ascx file and replace the default code. Partial Class DynamicData_FieldTemplates_IntegerSlider_EditField Inherits
System.Web.DynamicData.FieldTemplateUserControl Protected Sub
Page_Load(ByVal sender As Object, ByVal e As EventArgs) Dim metadata =
MetadataAttributes.OfType(Of
System.ComponentModel.DataAnnotations.RangeAttribute).FirstOrDefault() If Not metadata Is Nothing
Then SliderExtender1.Minimum = metadata.Minimum SliderExtender1.Maximum = metadata.Maximum End If TextBox1.ToolTip =
Column.Description SetUpValidator(DynamicValidator1) End Sub Protected Overrides Sub
ExtractValues(ByVal dictionary As IOrderedDictionary) dictionary(Column.Name) = ConvertEditedValue(TextBox1.Text) End Sub Public Overrides
ReadOnly Property DataControl() As Control Get Return TextBox1 End Get End Property End Class Figure 7: Open the
IntegerSlider_Edit.ascx.vb file and replace the default code. Field templates must derive from the
FieldTemplateUserControl class. This parent class provides a number of helper
properties and methods that perform work that is common to all field templates.
The most important of these members is the Column property, which contains
information about the column with which this field template is associated. The
property provides information about the column s metadata (that is, information
about the column in the data model) and provides access to the Dynamic Data
abstraction of the entire data model. The IntegerSlider_Edit.ascx markup (see Figure 6) is
simple. The TextBox1 control is the main data control and contains the value
that will be used for the data field on postback. Label1 is an auxiliary
control that simply displays the current value of the data field as the user
interacts with the slider. SliderExtender1 is the component from the AJAX
Control Toolkit that turns TextBox1 into a slider. It is associated with
TextBox1 and Label1 via their IDs. The only unfamiliar control is the DynamicValidator
control. This is a control specific to Dynamic Data that catches any validation
errors that might be thrown by the data model, then displays error messages in
the UI. For example, a LINQ-to-SQL model enables you to add custom validation
logic using the partial extension method mechanism, as illustrated later in
this article. The validation logic can throw a ValidationException error if a
property or object fails validation. When the user submits invalid data, the
DynamicValidator control catches the validation exception and displays its own
error message to the user. Field templates are designed to behave like any other
data-bound control, and they therefore support ordinary data-binding syntax to
populate values in the controls. The base FieldTemplateUserControl class
provides the FieldValue property to retrieve the raw value object. The FieldValueString
and FieldValueEditString properties return formatted and HTML-encoded strings
for display and edit purposes, respectively. In addition, the Row property can
be used to retrieve the entire data row. The code-behind file for the field template contains
initialization and data-binding logic. The IntegerSlider_Edit.ascx.vb example (see
Figure 7) illustrates how the slider s minimum and maximum allowed values are
specified based on metadata about the range information that is derived from
the model. The SetUpValidator method is used to configure all standard ASP.NET
validators (such as RequiredValidator) as well as DynamicValidator controls
with values appropriate to the data field that the template is being used to
display. This includes initializing default error messages and other parameters
based on the data model s metadata. Because the IntegerSlider template is an edit template,
during postback you must get the value that the user has entered. To do this,
override the ExtractValues method defined in the IBindableControl interface
that is implemented by the FieldTemplateUserControl class. The ExtractValues
method gets the modified data and populates a dictionary with the new values.
This dictionary will later be passed to a data source control that will in turn
perform the appropriate update operation. The example field template also
overrides the DataControl property, which can be used in pages that contain
DynamicField or DynamicControl controls to obtain a reference to the actual
control that is responsible for displaying the data. For example, this can be
used to associate a Label control declared on a page with the instance of the
data control (such as a TextBox) that is declared in the field template. The IntegerSlider_Edit.ascx name does not match the naming
convention used by Dynamic Data to choose the appropriate template by
default, when Dynamic Data displays editing UI for integer data, it will look
for the field template named Integer_Edit.ascx. To get Dynamic Data to use the
custom IntegerSlider_Edit.ascx template, you must add metadata to the data
model that maps a specific data column to your new field template. In Dynamic Data, you add metadata to the data model in the
form of an associated metadata class. Figure 8 illustrates how you apply
metadata for this example. The code assumes that you have a data model class
named Widget that has properties named LowRange and HighRange, and that you
want to use the IntegerSlider_Edit.ascx field template to edit the data in this
class. Partial Public Class Widget End Class Public Class WidgetMetadata Public ReadOnly
Property Id() As Object Get Return Nothing End Get End Property Public ReadOnly
Property Name() As Object Get Return Nothing End Get End Property Public ReadOnly
Property LowRange() As Object Get Return Nothing End Get End Property Public ReadOnly
Property HighRange() As Object Get Return Nothing End Get End Property End Class Figure 8: How to apply
metadata. The primary purpose of the Widget partial class is to
provide a place to apply attributes that extend the data model; therefore, it
contains no actual logic. However, its name must match the name of the
data-model type you want to extend. The WidgetMetadata class in turn contains
the actual logic for extending the data-model type. To extend individual data
columns, you create properties in the metadata class that match the data column
names. The MetadataTypeAttribute applied to the Widget partial
class specifies the type to use as the metadata class (here, WidgetMetadata
class). The attributes applied to the properties of the metadata type are
treated by Dynamic Data as if they were applied directly to matching properties
of the actual data-model type. In this example, the LowRange and HighRange
properties of the Widget class each have the RangeAttribute and UIHintAttribute
attributes applied. UIHintAttribute overrides the default field-template
mapping. In this case, Dynamic Data will try to load the
IntegerSlider_Edit.ascx control when a field template is requested in order to
produce UI for editing the LowRange or HighRange column values. RangeAttribute
declares the range of values for a specific column. It is used by the field
template initialization method to set the bounds for the SliderExtender
control. RangeAttribute inherits from ValidationAttribute. The
ValidationAttribute class encapsulates validation checks. Therefore,
RangeAttribute can perform its own validation checks. This ensures that even if
a client is able to bypass the range limitations set by the slider UI and
submit an invalid value, the submitted value will be validated on the server by
RangeAttribute and result in an error. These errors will then be captured and
displayed by the DynamicValidator control. Field templates are a powerful feature that provide a
compelling reason to use Dynamic Data, even in existing ASP.NET Web
applications. The ability to extend the data model, combined with centralized
field templates, significantly reduces the need for code (and maintenance) in
creating data-bound UI. By creating a suite of field templates that define the
UI for your needs, you can get a sophisticated, highly functional Web
application UI up and running in no time, and with very little coding. Source code
accompanying this article is available for download. Marcin
Dobosz is a developer on the ASP.NET
team. He currently is working on the Dynamic Data feature. You can find him
online at http://blogs.msdn.com/MarcinOn/. Scott
Hunter is a program manager on the ASP.NET
team. He currently is working on the Dynamic Data feature. Scott has been
working in the industry for more than 20 years and has spent the past seven
years building Web applications on the ASP.NET
platform. You can find him online at http://blogs.msdn.com/scothu/. Annotating the Data Model with Metadata
Conclusion