Click to Rate and Give Feedback
Related Articles

Cobra, a descendant of Python, offers a combined dynamic and statically-typed programming model, built-in unit test facilities, scripting capabilities, and much more. Feel the power here.

Ted Neward

MSDN Magazine June 2009

...

Read more!

Silverlight 2 applications are restricted to running inside a browser. However, Silverlight 3 applications can run inside the browser or out. Here we build a social networking app as a standalone Silverlight 3 application.

John Papa

MSDN Magazine June 2009

...

Read more!

Memory usage can have a direct impact on how fast an application executes and thus is important to optimize. In this article we discuss the basics of memory optimization for .NET programs.

Subramanian Ramaswamy and Vance Morrison

MSDN Magazine June 2009

...

Read more!

In this month's column, we’ll explore the pros and cons of both ASP.NET Web Forms and ASP.NET MVC.

Dino Esposito

MSDN Magazine July 2009

...

Read more!

Use Test-Driven Development with mock objects to design object oriented code in terms of roles and responsibilities, not categorization of objects into class hierarchies.

Isaiah Perumalla

MSDN Magazine June 2009

...

Read more!

Also by this Author

Choosing the right design pattern for your ASP.NET Web application can help you achieve the separation of concerns between your presentation layer and the layers beneath it.

Dino Esposito

MSDN Magazine December 2008

...

Read more!

This month Dino builds a service layer that authenticates users of Silverlight 2 and ASP.NET AJAX services to prevent illegal access to sensitive back-end services.

Dino Esposito

MSDN Magazine September 2008

...

Read more!

There’s a strong similarity between Web-based Silverlight 2 applications and desktop WPF applications. Enabling easy code reuse between the two is Dino’s focus here.

Dino Esposito

MSDN Magazine October 2008

...

Read more!

Achieving cross-browser compatibility for events is no easy task. The jQuery event handling API addresses the differences in event handling across browsers, allowing you to write more predictable JavaScript.

Dino Esposito

MSDN Magazine April 2009

...

Read more!

This month, use nested ListView controls to create hierarchical views of data and extend the eventing model of the ListView by deriving a custom ListView class.

Dino Esposito

MSDN Magazine April 2008

...

Read more!

Popular Articles

WPF is one of the most important new technologies in the .NET Framework 3.0. This month John Papa introduces its data binding capabilities.

John Papa

MSDN Magazine December 2007

...

Read more!

We introduce you to the benefits of building composite applications with the Composite Application Guidance for WPF from Microsoft patterns & practices.

Glenn Block

MSDN Magazine September 2008

...

Read more!

Here we present techniques for programmatic and declarative data binding and display with Windows Presentation Foundation.

Josh Smith

MSDN Magazine July 2008

...

Read more!

Chris Tavares explains how the ASP.NET MVC Framework's Model View Controller pattern helps you build flexible, easily tested Web applications.

Chris Tavares

MSDN Magazine March 2008

...

Read more!

Jeff Prosise explains when it's better to use UpdatePanel and when it's better to use asynchronous calls to WebMethods or page methods instead.

Jeff Prosise

MSDN Magazine June 2007

...

Read more!

Cutting Edge
Custom Data Control Fields
Dino Esposito

Code download available at: CuttingEdge0601.exe (131 KB)
Browse the Code Online
In ASP.NET 2.0, the GridView and DetailsView controls are designed to work together. They don't merely provide complementary services, they also share a number of helper classes and components. The output of the GridView control consists of a sequence of rows, each with a fixed number of columns. Each table column maps to a data column in the bound data source. The DetailsView control has a fixed number of rows (one for each column in the bound data source) and a constant number of columns (header and value).
GridView and DetailsView can be used together to form master/details pages, as I demonstrated in my last column (see Cutting Edge: Flexible Custom Data Views). Both represent data columns in the bound data source. In fact, a GridView's column and a DetailsView's row are the same kind of object and are represented in ASP.NET 2.0 by the same class—DataControlField.
DataControlField is an abstract class and cannot be used for actual data binding. A bunch of child field classes are derived for binding data to GridView and DetailsView controls. In my last column I covered some of these field types. In particular, I showed how to use the TemplateField class to add validation and special editing user interface capabilities to bound fields. The TemplateField class works as a generic container of markup. It is ideal for rendering custom types of data and any combination of existing fields that you might need. The TemplateField type can also be used to render foreign-key columns through dropdown lists or dates through calendars.
But what if you wanted a specialized, more compact calendar field class—a CalendarField type that would render the bound data as a string in view mode and employ a more effective Calendar control in edit and insert mode? As I mentioned, you can achieve the same result using a TemplateField, but that would be like using late-bound versus early-bound objects. A data-bound page based on CalendarField will be easier to read and even a bit faster to process at run time.
I'll discuss the design and implementation of custom data field types and demonstrate how to actually code them using a few examples—CalendarField to render date values and DropDownField to render foreign key values defined on an external data source.

The DataControlField Class
The DataControlField class in ASP.NET 2.0 is a parallel for the DataGridColumn type used extensively with DataGrid controls in ASP.NET 1.x and is used only by the GridView and DetailsView controls. However, if you happen to write a custom data-bound control that requires binding to columns of data, you can use the DataControlField type to bind data columns to your control's user interface.
As I mentioned, DataControlField is an abstract class that inherits directly from System.Object. It implements the IStateManager interface. Figure 1 lists the methods and properties on the class.

Property Description
AccessibleHeaderText Gets or sets text that is rendered as the AbbreviatedText property value in some controls for accessibility reasons.
ControlStyle Gets the style properties for any input control that the field uses in edit or insert mode.
FooterStyle Gets the style properties for the footer of the field. This is only applicable to the GridView control.
FooterText Indicates the text displayed in the footer of the field. This is only applicable to the GridView control.
HeaderImageUrl Gets or sets the URL reference to an image to display instead of text on the header of this field.
HeaderStyle Gets the style properties for the header of this field.
InsertVisible Indicates whether the field is displayed when the associated control is in Insert mode. This is only applicable to the DetailsView control.
ItemStyle Gets the style properties for an item of this field.
ShowHeader Indicates whether the field header is visible. This is only respected by the DetailsView control.
SortExpression Indicates the expression used when this field is used to sort the bound data source. This is only applicable to the GridView control.
Visible Indicates whether the field is visible.
Method Description
ExtractValuesFromCell Extracts the value from the control(s) in the field cell and adds it to the dictionary passed to this method, using a unique key name for this field.
Initialize Initializes the field.
InitializeCell Initializes a cell in the field.
ValidateSupportsCallback Indicates that the controls that are contained by the field support callbacks.
As you can see, the class supports a few style properties—ItemStyle, HeaderStyle, FooterStyle, and ControlStyle. In ASP.NET, style properties are not persisted in the view state through the usual ViewState container, as most simple type properties are. The Style class from which all style properties derive implements IStateManager to take care of view state persistence. To utilize this specific capability, classes that expose style properties typically implement IStateManager themselves to delegate view state persistence to the methods defined on the objects that represent style properties. I'll return to this later.
Types derived from DataControlField are listed in Figure 2. Although the DataControlField class is marked abstract, it contains a lot of code and many concrete members. The only abstract method is CreateField, defined as follows:
Protected MustOverride Function CreateField() As DataControlField

Class Description
BoundField Renders a simple string-based output for each element. It uses a TextBox control to capture input in edit/insert mode.
ButtonField Renders a command button for each element. Not supported in edit/insert mode.
CheckBoxField Renders a checkbox control for each element. A CheckBox control is used both in view and edit/insert mode.
CommandField Renders command buttons to perform selecting, editing, inserting, or deleting operations.
HyperLinkField Renders a hyperlink control for each element. Not supported in edit/insert mode.
ImageField Renders an image for each element from the bound URL. The field is predisposed to display BLOB data containing images. In edit/insert mode, it uses a TextBox control to edit the image URL.
TemplateField Renders some custom UI for each element. It allows you to define a template for the edit mode and one for the insert mode.
This method is expected to return an instance of the particular data control field object. Other methods should also be overridden in derived classes because their built-in implementation is empty (though not abstract). Those methods are ExtractValuesFromCell and InitializeCell. You must override them to create a significant new field type. Depending on what kind of new functionality you want to build into the field, overriding additional members might be required as well.

Designing the CalendarField Component
The CalendarField component inherits from DataControlField and adds three new properties—DataField, ReadOnly, and DataFormatString. DataField is a string property and indicates the name of the data source column that will be bound to the field. ReadOnly is a Boolean property to indicate whether the contents of the field are editable. To control whether the field is to be added in the insert mode UI, you can use the base InsertVisible Boolean property. Finally, DataFormatString indicates the desired format of the date. The contents of the DataFormatString property will be passed to the ToString method of the DateTime class, meaning that {0:d} and similar expressions are invalid. Here's an example of the CalendarField persistence format:
<msdn:CalendarField DataField="OrderDate" 
      HeaderText="Date" ShowHeader="true"
      DataFormatString="dd MMM, yyyy" ReadOnly="False" />
The code that follows shows the implementation of the DataField property (keep in mind that the other custom properties follow the same implementation scheme):
Public Overridable Property DataField As String
      Get
         Dim o As Object = MyBase.ViewState("DataField")
         If (Not o Is Nothing) Then Return CType(o, String)
         Return String.Empty
      End Get
      Set(ByVal value As String)
         MyBase.ViewState("DataField") = value
         OnFieldChanged()
      End Set
End Property
The OnFieldChanged method is a protected virtual method defined on the parent class DataControlField and meant to signal to the host control (the GridView) that the value of a property on the field has changed. Each control field component knows about its host control through the Control property defined on DataControlField. The Control property is protected and set when the host control initializes the field component.
The Initialize method (see Figure 1) is called by the host control (GridView or DetailsView) to initialize the field. The initialization takes place before the field is added to the Fields collection of GridView and DetailsView. GridView and DetailsView call the Initialize method from within their CreateChildControls method. The Initialize method is given its basic behavior in the DataControlField base class and there's no real need for you to override it in a derived class:
Public Overridable Function Initialize( _
        sortingEnabled As Boolean, ctl As Control) As Boolean
Initialize receives a Boolean value that indicates if sorting is supported. It also receives a reference to the host control (GridView or DetailsView) to be stored in the internal Control property. The return value tells the host control if a call to DataBind is required to complete the initialization of the field type.
Another method on DataControlField that you might want to override in some cases is ValidateSupportsCallback. The default implementation of the method in DataControlField simply throws an exception that the field type doesn't intend to support script callbacks. In a derived class you can override this method to prevent the exception from being thrown in some or all cases. For example, you can supply an empty method implementation to say that a field supports callbacks or throws an exception based on the value of a field-specific property:
Public Overrides Sub ValidateSupportsCallback()
   If Not SupportsCallback Then
      Throw New NotSupportedException("Callbacks Not Supported")
   End If
End Sub
InitializeCell and ExtractValuesFromCell will need overrides in any significant control. InitializeCell lets you take control of the contents of cell where the field value is displayed. ExtractValuesFromCell extracts values from the cell to pursue updates or insert operations. Before taking the plunge into these methods and their overrides for the CalendarField component, let's have a look at Figure 3 where other protected overridable methods are listed.

Overridable Method Description
CopyProperties Receives an object reference that represents a copy of the current field object and adds class-specific properties.
CreateField Abstract method, returns an instance of the field type.
LoadViewState Restores the class style properties from the view state.
OnFieldChanged Fires the FieldChanged event defined on the DataControlField class.
SaveViewState Saves the class style properties to the view state.
TrackViewState Enables view state tracking for the style properties.

Implementing CalendarField
The CalendarField class provides the three aforementioned properties—DataField, ReadOnly, and DataFormatString—and overrides InitializeCell, ExtractValuesFromCell, CopyProperties, and CreateField.
CreateField follows a common scheme and gets the same form of implementation in all built-in field types:
Protected Overrides Function CreateField() As DataControlField
   Return New CalendarField()
End Function
Overriding this method is mandatory if you derive from DataControlField; it is optional, but recommended, if you derive from existing field types.
In InitializeCell you begin by calling the base method to import much of the logic that deals with header and footer rendering. Next, you add some code to customize the current cell, as shown here:
Sub InitializeCell(cell As DataControlFieldCell, _
        cellType As DataControlCellType, _
        rowState As DataControlRowState, rowIndex As Integer)

     ' Call the base method
     MyBase.InitializeCell(cell, cellType, rowState, rowIndex)

     ' Initialize the contents of the cell 
     If cellType = DataControlCellType.DataCell Then
          InitializeDataCell(cell, rowState)
     End If
End Sub
The InitializeCell method receives four arguments: a reference to the cell object, the type of the cell, and the state and index of the row being rendered. Most of the existing field components delegate their rendering tasks to another internal method that is usually named InitializeDataCell. Figure 4 shows the implementation of InitializeDataCell for CalendarField.
Protected Overridable Sub InitializeDataCell( _
        ByVal cell As DataControlFieldCell, _
        ByVal rowState As DataControlRowState)
    Dim ctrl As Control = Nothing

    ' If in edit/insert mode...
    Dim state As DataControlRowState = rowState & DataControlRowState.Edit
    If ((Not Me.ReadOnly And (state <> DataControlRowState.Normal)) Or _
        rowState = DataControlRowState.Insert) Then

        Dim cal As New Calendar
        cal.ToolTip = HeaderText
        cell.Controls.Add(cal)

        ' Save the control to use for binding (edit/insert mode)
        If DataField.Length > 0 Then ctrl = cal

        _inInsertMode = (rowState = DataControlRowState.Insert)

    ElseIf (DataField.Length > 0) Then
        ' Save the control to use for binding (view mode)
        ctrl = cell
    End If

    ' If the column is visible, trigger the binding process
    If Not ctrl Is Nothing And Visible Then
        AddHandler ctrl.DataBinding, New EventHandler(OnBindingField)
    End If
End Sub

Protected Overridable Sub OnBindingField( _
        ByVal sender As Object, ByVal e As EventArgs)
    Dim target As Control = CType(sender, Control)

    ' If in view mode ...
    If TypeOf (target) Is TableCell Then
        Dim t As TableCell = CType(target, TableCell)
        t.Text = LookupValueForView(target.NamingContainer)
    ElseIf TypeOf (target) Is Calendar Then
        Dim cal As Calendar = CType(target, Calendar)
        Dim dt As DateTime = LookupValueForEdit(target.NamingContainer)
        cal.SelectedDate = dt
        cal.VisibleDate = dt
    End If
End Sub
At its core, the InitializeDataCell method determines which control to bind to data—the cell for view mode or a Calendar control for edit or insert modes. The control is then attached to a DataBinding event handler to actually retrieve and display data. In the handler, you determine the working mode, retrieve the value to display, and configure the control as appropriate.
In view mode, the LookupValueForView helper method determines the value to display. It takes a reference to the naming container of the control (table cell or calendar) that currently forms the field's UI. The naming container is passed to the DataBinder.GetDataItem method to obtain a reference to the row object:
Dim dataItem As Object = DataBinder.GetDataItem(container)
Dim dt As DateTime = _
    CType(DataBinder.GetPropertyValue(dataItem, DataField), DateTime)
Next, DataBinder.GetPropertyValue extracts the requested field from the row object. For a CalendarField control, this value is a DateTime object and can be further formatted before display. In edit or insert mode, no significantly different behavior is required, except that you might want to distinguish between edit and insert and provide a default value in the latter case:
Protected Overridable Function LookupValueForEdit( _
        container As Control) As DateTime
    If Not _inInsertMode Then
        Dim dataItem As Object = DataBinder.GetDataItem(container)
        Dim value As Object = DataBinder.GetPropertyValue( _
            dataItem, DataField)
        Return CType(value, DateTime)
    End If
    Return DateTime.Now
End Function
GridView and DetailsView controls scan their own list of bound fields to collect input values when an update or insert command is selected. In doing so, they call the ExtractValuesFromCell method on the field types. Figure 5 details the implementation of ExtractValuesFromCell in the CalendarField component.
Public Overrides Sub ExtractValuesFromCell( _
        ByVal dictionary As IOrderedDictionary, _
        ByVal cell As DataControlFieldCell, _
        ByVal rowState As DataControlRowState, _
        ByVal includeReadOnly As Boolean)

    Dim selectedValue As Object

    If cell.Controls.Count > 0 Then
        Dim cal As Calendar = CType(cell.Controls(0), Calendar)

        If cal Is Nothing Then
            Throw New InvalidOperationException( _
                "CalendarField could not extract control.")
        Else
            selectedValue = cal.SelectedDate
        End If
    End If

    ' Add the value to the dictionary
    If dictionary.Contains(DataField) Then
        dictionary(DataField) = selectedValue
    Else
        dictionary.Add(DataField, selectedValue)
    End If
End Sub
The ExtractValuesFromCell method is invoked when the field is in edit or insert mode. You locate the input control and capture any significant values it may contain. Based on the code shown in Figure 4, the CalendarField's input control is the first control in the Controls collection of the cell. (Note that the position of the input control is arbitrary and depends on the structure of the cell you create in InitializeCell.)
Once you hold the currently selected value of the input control, you add it to the dictionary object that was passed to the ExtractValuesFromCell method. The dictionary contains name/value pairs where the name entry is the name of the DataField property.

Using the CalendarField Component
A custom data control field must be registered with the page (or the application) as any other custom server control. To do this, use the @ Register directive like so:
<%@ Register Namespace="Samples.CustomFields" 
    TagPrefix="expo" Assembly="HelperFields" %>
Add the following code to the <fields> element of a DetailsView or GridView:
<msdn:CalendarField DataField="OrderDate" HeaderText="Date" />
Turn the page in design mode and work through the Visual Studio® 2005 menus and windows to get what's shown in Figure 6.
The sample DetailsView has two custom data fields: CalendarField, used for the Date row, and a DropDownField, used for the posted-by row. I'll say more about the DropDownField control shortly. As you can see, the two control fields have a slightly different user interface. This is due to the DataBinding event handler. Basically, you check the DesignMode property and output some markup if the output is going to be shown in Visual Studio 2005:
If DesignMode Then
   Return "<select><option>Databound Date</option>           </select>"
End If
Figure 6 DetailsView 
DesignMode is a protected property of the DataControlField class. It wraps the DesignMode property of the host control. The code you just saw returns a dropdown-like user interface and characterizes the custom control field. Although the custom data control fields have to be manually coded in the editor, you have full visual support as far as properties editing is concerned. Smart tags and the Properties dialog box show custom fields correctly.
Figure 7 shows a sample master/detail page in action. The DetailsView control uses the CalendarField component to display the date of a data-bound order.
Figure 7 Custom Data Fields in Action—Edit Mode 

Designing a DropDownField Component
The DropDownField data field is used with foreign-key fields, that is with data columns that contain indexes to data living in another table. Imagine a table of orders with columns to track the customer who made the order and the employee who physically placed the order. In the Orders table, you won't typically store the full name of employee and customer; rather, you'd store a pointer to another table. In the end, a row in the Orders table contains numbers instead of plain names. This might be great for database management systems, but it's not great for human consumers. When displaying data, you need to transform those numbers into readable names. This is only half the task that you might expect from a DropDownField component. When in edit or insert mode, in fact, the data field should be able to display a list of possible options—that is, all possible customers or employees.
I designed the DropDownField component to expose DataTextField and DataValueField properties. The former indicates the data column to use for display purposes; the latter indicates the data column to use for actual I/O data exchange. The DataTextField is useful only in one particular scenario: when your query contains joined columns. Consider the following SQL command:
SELECT o.*, e.lastname FROM orders o
INNER JOIN employees e on o.employeeid=e.employeeid 
WHERE o.orderid=@id
A row from this query that is bound to a DetailsView control contains both the ID of the employee who placed the order (table Orders) and her last name (joined table Employees). In this case, you set DataValueField to employeeid and DataTextField to lastname. If you don't have joined columns, you just leave DataTextField blank.
The DropDownField component also counts three more properties. DataSourceIDForEdit indicates the data source control providing the options to list in edit/insert mode. DataValueFieldForEdit denotes the field in the data source used to determine the value of the items in the dropdown list. Finally, DataTextFieldForEdit denotes the field in the data source used to determine the display text of items in the dropdown list.
To a large extent, the code for DropDownField is the same as for CalendarField. A key difference exists when the data binding is made. To prepare the calendar control, you only need to set its SelectedDate property with the value you obtain from the bound row. With a DropDownField, you first need to populate the dropdown list and select the item that corresponds to the DataValueField's value in the bound row. The dropdown list control is bound to the list of feasible data items through the DataSourceID property. For data binding, this would work just fine. However, setting DataSourceID on a data-bound control doesn't automatically start the binding process. If you try to access the Items collection immediately after setting DataSourceID, you'll get a null reference exception. If you call DataBind to force binding, you get a stack overflow exception because you're calling DataBind from within a DataBinding event, which is triggered by a call to DataBind. The trick is to attach a DataBound event handler to the dropdown list control:
Dim dd As DropDownList = CType(target, DropDownList)
dataValue = LookupValueForEdit(target)
AddHandler dd.DataBound, AddressOf OnDropDownDataBound
dd.DataTextField = DataTextFieldForEdit
dd.DataValueField = DataValueFieldForEdit
dd.DataSourceID = DataSourceIDForEdit
The DataBound event is new in ASP.NET 2.0 for data-bound controls and is fired when the data binding process is complete. In this way, the event handler for DataBound fires when the Items collection is fully populated and you can select the item safely:
Sub OnDropDownDataBound(sender As Object sender, e As EventArgs)
   Dim dd As DropDownList = CType(sender, DropDownList)
   Dim li As ListItem = dd.Items.FindByValue(dataValue)
   li.Selected = True
End Sub
A key goal of the DropDownField component is to be able to use the bound value, DataValueField, to find a related field, possibly in another data source. This operation implies a database query, an INNER JOIN operation, or an access to the ASP.NET cache where related data is cached. Running a real query against a database is simply out of the question for very good reasons: for performance and because it would break consistency and force you to inject connection string and SQL information in a data control field. The only reasonable approach entails retrieving the related field (for example, the last name of the employee knowing the ID) from the data source used to populate the dropdown list in edit mode (you populate the dropdown list when the data control field is initialized). To minimize the performance hit, the data source should be accessed only when you enter into edit/insert mode. Caching should be enabled on the data source control and a cache key dependency mechanism should be used to refresh the cached data in case of changes.
What if the row bound to the DetailsView contains a joined field? In this case, the required information is already available. That's why I defined an optional DataTextField property. If it is set, and the specified field really exists, the data source for edit is loaded only when the host control really enters in edit/insert mode. If DataTextField is not available, then the data source is accessed at initialization time, and the input dropdown list control is created earlier and made available to internal methods through a private property. Here's the markup code that binds a DropDownField field to the employeeid column of the Orders table and the lastname column in the Employees table:
<msdn:DropDownField 
     DataValueField="employeeid" 
     HeaderText="Posted by" 
     DataSourceIDForEdit="EmployeeDataSource" 
     DataValueFieldForEdit="employeeid"
     DataTextFieldForEdit="lastname" />
EmployeeDataSource is defined in the following:
<asp:SqlDataSource ID="EmployeeDataSource" runat="server" 
   ConnectionString='<%$ ConnectionStrings:LocalNWind %>'
   SelectCommand="SELECT employeeid, lastname FROM 
   employees" />
The full source code of DropDownField and CalendarField is available with the download for this issue of MSDN®Magazine.
As a side note, remember that the DropDownField component that is discussed here is optimized for data-driven sources. However, the DropDownField component could easily be used to allow users to select any value out of a fixed list—for example, an enum type or any enumerable collection of data. To support these scenarios, you might want to add a DataSource property and bind its contents to the DataSource property of the dropdown list control programmatically.

Styling Control Fields
The DataControlField type defines a few style properties for header, footer, and data items. In addition, the ControlStyle property lets you style the input controls used by the field component:
<asp:BoundField DataField="CompanyName" HeaderText="Company">
     <ControlStyle BackColor="red" />
</asp:BoundField>
The preceding code snippet generates a bound field to view or edit the CompanyName data column. The internal textbox of the field will have a red background. When writing custom data control fields, you can use the ControlStyle property to style input controls in a polymorphic way. Both the Calendar and the dropdown list controls, in fact, can be styled through the ControlStyle property. By assigning default values to the property, you can also set new default settings for the input controls.
Depending on characteristics and capabilities of the input controls, you may need to define custom style properties. A new style property can be of type TableItemStyle or a new custom type derived from TableItemStyle. Finally, any new style property you add must be properly persisted in the view state. You need to override LoadViewState, SaveViewState, and TrackViewState methods. Figure 8 shows how to proceed for a sample YourStyle property.
Protected Overrides Sub LoadViewState(savedState As Object)
   If Not savedState Is Nothing Then
      Dim state As Object() = CType(savedState, Object())
      If Not state(0) Is Nothing Then 
         MyBase.LoadViewState(state(0))
      End If
      If Not state(1) Is Nothing Then
         YourStyle.LoadViewState(state(1))
      End If
   End If
End Sub

Protected Overrides Sub TrackViewState()
   MyBase.TrackViewState()
   YourStyle.TrackViewState()
End Sub
For the sample CalendarField field, custom style properties can be defined for any of the calendar style you want to control from the page level. The ControlStyle property in this case is of little help because it can only address the style of the calendar core element. In order to style internal parts of the helper calendar control, you need to expose some of the internal styles of the Calendar through new properties.

Conclusion
DataControlField is the base class for data fields—helper components that GridView and DetailsView controls use to build their own data-bound user interface. DataControlField has several derived classes, ImageField, CheckBoxField, and the more popular TemplateField and BoundField, that can be used in pages and ASP.NET applications. In addition, you can create custom data fields. Two canonical examples are DropDownField for foreign keys and CalendarField for dates. In addition to the description of these two components given here, the documentation for DataControlField contains another sample field—the RadioButtonField. In the documentation code, the RadioButtonField inherits from CheckBoxField and provides an alternate user interface for Boolean choices. Look there if you want to explore this topic further.

Send your questions and comments for Dino to  cutting@microsoft.com.


Dino Esposito is a mentor at Solid Quality Learning and the author of Programming Microsoft ASP.NET 2.0 (Microsoft Press, 2005). Based in Italy, Dino is a frequent speaker at industry events worldwide. Get in touch with Dino at cutting@microsoft.com or join the blog at weblogs.asp.net/despos.

Page view tracker