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;
}
}