Creating Custom Data Field Controls - Part 1
page 2 of 5
by Brian Mains
Feedback
Average Rating: This article has not yet been rated.
Views (Total / Last 10 Days): 26297/ 31

Data Control Field Essentials

The rows of the GridView control have various styles to them. A GridView row has a header and footer (optionally) that appear at the top and bottom. Between them is the data rows, not taking into effect the paging row, where the pager renders. Data rows can render in normal mode, as well as show a separate style for the alternating row. The GridView can support in-line editing, which shows a completely different interface, and although not directly supported, custom grids also support insertion (and the DetailsView does as well). These are the various interactions to be aware of. Most specifically, the data rows are the pieces you need to manage; headers and footers are managed for you.

When a row is rendered or even selected in a GridView, the cell being rendered is controlled by the DataControlField class that exposes it. The underlying data source data is embedded in a table cell, or a control that represents the interface. In order to use this data in a more meaningful way, the ExtractValuesFromCell method can be used to get the value from the underlying control, and pass it to an ordered dictionary (discussed in the next article).

Listing 1

public override void ExtractValuesFromCell(IOrderedDictionary dictionary, 
  DataControlFieldCell cell, DataControlRowState rowState, bool includeReadOnly)
{
      base.ExtractValuesFromCell(dictionary, cell, rowState, includeReadOnly);
      string value = null;
 
      if (cell.Controls.Count > 0)
            value = ((TextBox)cell.Controls[0]).Text;
 
      //DataField specifies the name of the property bound
      if (dictionary.Contains(DataField))
            dictionary[DataField] = value;
      else
            dictionary.Add(DataField, value);
}

The DataControlField base class handles a lot of the plumbing of this process for you; however, to create the interface of a custom data field, InitializeCell must be overridden to handle creating the user interface and adding any controls to the table cell.

Listing 2

public override void InitializeCell(DataControlFieldCell cell, 
  DataControlCellType cellType, DataControlRowState rowState, int rowIndex)
{
      base.InitializeCell(cell, cellType, rowState, rowIndex);
      Control control = null;
 
      if (cellType == DataControlCellType.DataCell)
      {
            if (this.IsReadMode(rowState))
                  control = cell;
            else
            {
                  TextBox box = new TextBox();
                  box.Columns = 30;
                  cell.Controls.Add(box);
 
                  if (!string.IsNullOrEmpty(DataField))
                        control = cell.Controls[0];
            }
 
            if (control != null && this.Visible)
                  control.DataBinding += new EventHandler(control_DataBinding);
      }
}

Remember that InitializeCell initializes the interface of the cell and taps into data binding, whereas ExtractValuesFromCell extracts the values from the cell or inner controls. You do not need to control how this occurs (a benefit to this polymorphic approach); you only need to worry about getting data in and out, and rendering the correct interface.

Next comes the issue of data access; the DataBinder's GetPropertyValue method makes it handy to extract the actual value from the data source, meaning that you do not need to perform that actual work or need to determine whether it is a DataTable, DataSet, business object collection, or something else that is enumerable. This method handles the data source inspection for you, taking the name of the property or column, and returns the value found (or an exception if that property does not exist). Note the call to GetDataItem, which actually gets the reference to the targeted data item.

Listing 3

object dataItem = DataBinder.GetDataItem(cell.NamingContainer);
 value = DataBinder.GetPropertyValue(dataItem, this.DataField);

For ease of use, I created a custom base class for data control fields with a set of primitive methods to makeup the core functionality. The following method makes it easier to extract a value from the underlying data source. Let us take a look at the method below.

Listing 4

protected object GetDataItemValue(object container, string propertyName) 
 {
   if (container == null || this.DesignMode) return null; 
   //Get the data item from the container
 
   object dataItem = DataBinder.GetDataItem(container); 
   if (dataItem == nullreturn null;
   return DataBinder.GetPropertyValue(dataItem, propertyName);
 }

This method takes a naming container (often passed in through tableCell.NamingContainer or control.NamingContainer) as a property, which is used to reflect on the data item. Also required is the property name to reflect against. The property value is a name of a property in a business object, a name of a column in a data table, or some other attribute that is used to retrieve the value from.

The last portion of the method above is meant to provide some flexibility with this scenario. For instance, if a business object employee has a property named Address (of type PersonalAddress), which has its own properties, it would be helpful to provide a string Address.Line1 to reference the first line of the address, rather than to have to use a TemplateField and explicit casts. The solution to this is to break apart an expression by the period, then reflect against each term individually. If a null is returned for some reason, the trail ends and a null is returned; otherwise, the final value is returned. This makes for a handy fix to a common problem.

The next task that must be discussed is data binding. Take a look at the following partial implementation of InitializeCell, which sets up the interface.

Listing 5

if (cellType == DataControlCellType.DataCell){
     //IsReadMode determines if this is readonly mode (normal, alternate, or
    //selected row, not editing or inserting)
     if (this.IsReadMode(rowState)) 
        control = cell;
    else
    {
        //SetupEditControl returns an instance of the control that will 
        //render in insert/edit mode
         cell.Controls.Add(this.SetupEditControl());
         if (!string.IsNullOrEmpty(this.GetDataItemFieldName())) 
            control = cell.Controls[0];
    }
 
 
    if (control != null && this.Visible)
        control.DataBinding += new EventHandler(control_DataBinding); 
}

View Entire Article

User Comments

Title: A test title   
Name: Hassan
Date: 2008-06-30 10:37:25 AM
Comment:
very good article.
Title: Awesome!   
Name: AzamSharp
Date: 2008-06-26 12:17:36 AM
Comment:
Hi,

Very nice article! I think I am going to look into creating some custom data field controls myself.
Title: Nice article   
Name: Joydip Kanjilal
Date: 2008-01-04 12:16:56 PM
Comment:
Nice article Brian.

Thanks,

Joydip

Author: ASP.NET Data Presentation Controls Essentials (Packt Publishing)

http://www.packtpub.com/asp-net-data-presentation-controls/book






Community Advice: ASP | SQL | XML | Regular Expressions | Windows


©Copyright 1998-2024 ASPAlliance.com  |  Page Processed at 2024-03-19 7:29:36 AM  AspAlliance Recent Articles RSS Feed
About ASPAlliance | Newsgroups | Advertise | Authors | Email Lists | Feedback | Link To Us | Privacy | Search