Using Generics and Inheritance to Simplify Development
page 8 of 9
by Brian Mains
Feedback
Average Rating: 
Views (Total / Last 10 Days): 35718/ 51

Example: Custom Data Control Fields

The DataControlField base class is a class used for the columns in a GridView and fields in a DetailsView control.  These fields provide the interface for a data bound field provided from a data source control or other type of data source.

The first level of data field base controls is the BaseDataField class.

Listing 8

namespace Nucleo.Web.Controls
{
  public abstract class BaseDataField: DataControlField
  {
    private bool _insertMode = false;
 
    public string DataField
    {
      get
      {
        object o = ViewState["DataField"];
        return (o == null) ? string.Empty : o.ToString();
      }
      set
      {
        ViewState["DataField"= value;
      }
    }
 
    protected bool InsertMode
    {
      get
      {
        return _insertMode;
      }
    }
 
    public bool ReadOnly
    {
      get
      {
        object o = ViewState["ReadOnly"];
        return (o == null) ? false : (bool)o;
      }
      set
      {
        ViewState["ReadOnly"= value;
      }
    }
 
    protected T ExtractValueFromControl < T > (TableCell cell, int index)where
      T: Control
    {
      if (index < 0 || index >= cell.Controls.Count)
        throw new ArgumentOutOfRangeException("index", Errors.OUT_OF_RANGE);
 
      Control control = cell.Controls[index];
      if (control == null)
        throw new InvalidOperationException(Errors.CANT_EXTRACT_CONTROL_IN_CELL)
          ;
      return (T)control;
    }
 
    public override void ExtractValuesFromCell(IOrderedDictionary dictionary,
      DataControlFieldCell cell, DataControlRowState rowState, bool
      includeReadOnly)
    {
      _insertMode = (rowState == DataControlRowState.Insert);
    }
 
    protected T GetDataItemValue < T > (object container, string property)
    {
      if (container == null || this.DesignMode)
        return default(T);
 
      object dataItem = DataBinder.GetDataItem(container);
      if (dataItem == null)
        return default(T);
      object value = DataBinder.GetPropertyValue(dataItem, property);
 
      if (value != null)
        return (T)value;
      else
        return default(T);
    }
 
    protected bool IsReadMode(DataControlRowState rowState)
    {
      return (this.ReadOnly || rowState == DataControlRowState.Normal ||
        rowState == DataControlRowState.Alternate || rowState ==
        DataControlRowState.Selected);
    }
  }
}

There are several standard fields when implementing a data field.  The DataField, InsertMode, and ReadOnly properties all have a specific function when rendering a data control field.  InsertMode is based off of the DataControlRowState setting of Insert specified from the ExtractValuesFromCell method.  DataField is commonly used because only one field is specified; there are alternatives if the data field uses multiple data fields.

To further minimize the amount of work to do, I created another base class.

Listing 9

namespace Nucleo.Web.Controls
{
  public abstract class BaseCustomDataField: BaseDataField
  {
    public abstract void BindEditControl(object control, bool insertMode);
 
    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 = this.GetEditControlValue(cell);
 
     //If the dictionary contains the data field, overwrite the value
      if (dictionary.Contains(this.DataField))
        dictionary[this.DataField] = value;
     //Create a new dictionary entry
      else
        dictionary.Add(this.DataField, value);
    }
 
    public abstract string GetEditControlValue(TableCell cell);
 
    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
        {
          cell.Controls.Add(this.SetupEditControl());
 
          if (!string.IsNullOrEmpty(this.DataField))
            control = cell.Controls[0];
        }
 
        if (control != null && this.Visible)
          control.DataBinding += new EventHandler(control_DataBinding);
      }
    }
 
    public abstract Control SetupEditControl();
 
    void control_DataBinding(object sender, EventArgs e)
    {
      if (sender is TableCell)
      {
        TableCell cell = sender as TableCell;
        cell.Text = this.GetDataItemValue < string > (cell.NamingContainer);
      }
      else
        this.BindEditControl(sender, this.InsertMode);
    }
  }
}

This class makes use of the Template Method pattern, meaning that any derived class only has to override the BindEditControl, GetEditControlValue, and SetupEditControl methods.  SetupEditControl handles creating the control, BindEditControl handles the binding, and GetEditControlValue handles getting the value specified when in edit mode.  From this, it makes it even simpler to use the base controls.  The InitializeCell and ExtractValuesFromCell methods all have a standardly-used definition that makes use of BindEditControl, GetEditControlValue, and SetupEditControl methods.  And if that does not meet your needs, you can choose a lesser implementation from BaseDataField class.  Below are the implementations of those methods in a TextField class.  Note that the properties are omitted.

Listing 10

public class TextField: BaseCustomDataField
{
  protected override DataControlField CreateField()
  {
    return new TextField();
  }
  public override void BindEditControl(object control, bool insertMode)
  {
    TextBox box = control as TextBox;
//If not in insert mode, set the text property
    if (!insertMode)
      box.Text = this.GetDataItemValue < string > (box.NamingContainer);
  }
 
  public override string GetEditControlValue(TableCell cell)
  {
    return this.ExtractValueFromControl < TextBox > (cell).Text;
  }
 
  public override Control SetupEditControl()
  {
    TextBox box = new TextBox();
    box.TextMode = this.TextMode;
    box.Rows = this.Rows;
    box.Columns = this.Columns;
    box.MaxLength = this.MaximumLength;
    box.ToolTip = this.HeaderText;
    return box;
  }
}

View Entire Article

User Comments

Title: Using Generics and Inheritance to Simplify Development   
Name: Meena
Date: 2007-10-26 6:32:53 PM
Comment:
This article help me a lot to learn generics.

Thanks
Title: Great Article!   
Name: Mohammad Azam
Date: 2007-09-28 3:18:52 PM
Comment:
Hi Brain,

Great article as always! Keep up the good work.
Title: Using Generics and Inheritance to Simplify Development   
Name: Brain
Date: 2007-05-12 1:40:08 PM
Comment:
Outstanding article. It was very informative. Thanks dude.
- Uday.D






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


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