From 81b323cf3d27949a5c78d44bd016044be3197b6c Mon Sep 17 00:00:00 2001
From: xue <>
Date: Mon, 5 Feb 2007 21:55:51 +0000
Subject: Added renderer feature to TDataGrid.

---
 .gitattributes                                     |   1 +
 HISTORY                                            |   2 +-
 framework/Web/UI/WebControls/TBoundColumn.php      | 104 ++++++++--
 framework/Web/UI/WebControls/TButtonColumn.php     |   7 +-
 framework/Web/UI/WebControls/TCheckBoxColumn.php   |   5 +-
 framework/Web/UI/WebControls/TDataGrid.php         |  30 ++-
 framework/Web/UI/WebControls/TDataGridColumn.php   | 210 ++++++++++++++++-----
 .../Web/UI/WebControls/TDataGridItemRenderer.php   | 105 +++++++++++
 framework/Web/UI/WebControls/TDataList.php         |  18 ++
 .../Web/UI/WebControls/TDropDownListColumn.php     |   6 +-
 .../Web/UI/WebControls/TEditCommandColumn.php      |   3 +-
 framework/Web/UI/WebControls/THyperLinkColumn.php  |   5 +-
 framework/Web/UI/WebControls/TLiteralColumn.php    |   5 +-
 framework/Web/UI/WebControls/TRepeater.php         |  14 ++
 framework/Web/UI/WebControls/TTemplateColumn.php   | 120 +++++++++---
 15 files changed, 534 insertions(+), 101 deletions(-)
 create mode 100644 framework/Web/UI/WebControls/TDataGridItemRenderer.php

diff --git a/.gitattributes b/.gitattributes
index 27e7892b..0f8c234b 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -2020,6 +2020,7 @@ framework/Web/UI/WebControls/TCustomValidator.php -text
 framework/Web/UI/WebControls/TDataBoundControl.php -text
 framework/Web/UI/WebControls/TDataGrid.php -text
 framework/Web/UI/WebControls/TDataGridColumn.php -text
+framework/Web/UI/WebControls/TDataGridItemRenderer.php -text
 framework/Web/UI/WebControls/TDataList.php -text
 framework/Web/UI/WebControls/TDataListItemRenderer.php -text
 framework/Web/UI/WebControls/TDataSourceControl.php -text
diff --git a/HISTORY b/HISTORY
index 52079e7a..cb25d850 100644
--- a/HISTORY
+++ b/HISTORY
@@ -9,7 +9,7 @@ ENH: Ticket#519 - Update TActiveRecord implementation (Wei)
 ENH: Added PRADO_CHMOD constant so that users can specify the permission of PRADO-created directories (Qiang)
 ENH: Added Display property to TWebControl (Wei)
 ENH: Added TUser.getState() and setState() for storing user session data (Qiang)
-ENH: Added renderer feature to TRepeater and TDataList (Qiang)
+ENH: Added renderer feature to TRepeater, TDataList and TDataGrid (Qiang)
 
 Version 3.1.0 alpha January 15, 2007
 ====================================
diff --git a/framework/Web/UI/WebControls/TBoundColumn.php b/framework/Web/UI/WebControls/TBoundColumn.php
index 533bb24c..89de53bd 100644
--- a/framework/Web/UI/WebControls/TBoundColumn.php
+++ b/framework/Web/UI/WebControls/TBoundColumn.php
@@ -42,6 +42,50 @@ Prado::using('System.Web.UI.WebControls.TDataGridColumn');
  */
 class TBoundColumn extends TDataGridColumn
 {
+	/**
+	 * @return string the class name for the item cell renderer. Defaults to empty, meaning not set.
+	 * @since 3.1.0
+	 */
+	public function getItemRenderer()
+	{
+		return $this->getViewState('ItemRenderer','');
+	}
+
+	/**
+	 * Sets the item cell renderer class.
+	 *
+	 * If not empty, the class will be used to instantiate as a child control in the item cells of the column.
+	 *
+	 * @param string the renderer class name in namespace format.
+	 * @since 3.1.0
+	 */
+	public function setItemRenderer($value)
+	{
+		$this->setViewState('ItemRenderer',$value,'');
+	}
+
+	/**
+	 * @return string the class name for the edit item cell renderer. Defaults to empty, meaning not set.
+	 * @since 3.1.0
+	 */
+	public function getEditItemRenderer()
+	{
+		return $this->getViewState('EditItemRenderer','');
+	}
+
+	/**
+	 * Sets the edit item cell renderer class.
+	 *
+	 * If not empty, the class will be used to instantiate as a child control in the item cell that is in edit mode.
+	 *
+	 * @param string the renderer class name in namespace format.
+	 * @since 3.1.0
+	 */
+	public function setEditItemRenderer($value)
+	{
+		$this->setViewState('EditItemRenderer',$value,'');
+	}
+
 	/**
 	 * @return string the field name from the data source to bind to the column
 	 */
@@ -103,26 +147,52 @@ class TBoundColumn extends TDataGridColumn
 	 */
 	public function initializeCell($cell,$columnIndex,$itemType)
 	{
-		parent::initializeCell($cell,$columnIndex,$itemType);
+		$item=$cell->getParent();
 		switch($itemType)
 		{
+			case TListItemType::Item:
+			case TListItemType::AlternatingItem:
+			case TListItemType::SelectedItem:
+				if(($classPath=$this->getItemRenderer())!=='')
+				{
+					$control=Prado::createComponent($classPath);
+					if($control instanceof IItemDataRenderer)
+					{
+						$control->setItemIndex($item->getItemIndex());
+						$control->setItemType($item->getItemType());
+					}
+					$cell->getControls()->add($control);
+				}
+				else
+					$control=$cell;
+				$control->attachEventHandler('OnDataBinding',array($this,'dataBindColumn'));
+				break;
 			case TListItemType::EditItem:
-				$control=$cell;
 				if(!$this->getReadOnly())
 				{
-					$textBox=Prado::createComponent('System.Web.UI.WebControls.TTextBox');
-					$cell->getControls()->add($textBox);
-					$cell->registerObject('TextBox',$textBox);
-					$control=$textBox;
+					if(($classPath=$this->getItemRenderer())!=='')
+					{
+						$control=Prado::createComponent($classPath);
+						if($control instanceof IItemDataRenderer)
+						{
+							$control->setItemIndex($item->getItemIndex());
+							$control->setItemType($item->getItemType());
+						}
+						$cell->getControls()->add($control);
+					}
+					else
+					{
+						$control=Prado::createComponent('System.Web.UI.WebControls.TTextBox');
+						$cell->getControls()->add($control);
+						$cell->registerObject('TextBox',$control);
+					}
 				}
-				if(($dataField=$this->getDataField())!=='')
-					$control->attachEventHandler('OnDataBinding',array($this,'dataBindColumn'));
+				else
+					$control=$cell;
+				$control->attachEventHandler('OnDataBinding',array($this,'dataBindColumn'));
 				break;
-			case TListItemType::Item:
-			case TListItemType::AlternatingItem:
-			case TListItemType::SelectedItem:
-				if($this->getDataField()!=='')
-					$cell->attachEventHandler('OnDataBinding',array($this,'dataBindColumn'));
+			default:
+				parent::initializeCell($cell,$columnIndex,$itemType);
 				break;
 		}
 	}
@@ -135,14 +205,16 @@ class TBoundColumn extends TDataGridColumn
 	public function dataBindColumn($sender,$param)
 	{
 		$item=$sender->getNamingContainer();
-		$data=$item->getDataItem();
+		$data=$item->getData();
 		$formatString=$this->getDataFormatString();
 		if(($field=$this->getDataField())!=='')
 			$value=$this->formatDataValue($formatString,$this->getDataFieldValue($data,$field));
 		else
 			$value=$this->formatDataValue($formatString,$data);
-		if(($sender instanceof TTableCell) || ($sender instanceof TTextBox))
-			$sender->setText($value);
+		if($sender instanceof IItemDataRenderer)
+			$sender->setData($data);
+		else if($sender instanceof IDataRenderer)
+			$sender->setData($value);
 	}
 }
 
diff --git a/framework/Web/UI/WebControls/TButtonColumn.php b/framework/Web/UI/WebControls/TButtonColumn.php
index adf6879f..78b59a44 100644
--- a/framework/Web/UI/WebControls/TButtonColumn.php
+++ b/framework/Web/UI/WebControls/TButtonColumn.php
@@ -226,7 +226,6 @@ class TButtonColumn extends TDataGridColumn
 	 */
 	public function initializeCell($cell,$columnIndex,$itemType)
 	{
-		parent::initializeCell($cell,$columnIndex,$itemType);
 		if($itemType===TListItemType::Item || $itemType===TListItemType::AlternatingItem || $itemType===TListItemType::SelectedItem || $itemType===TListItemType::EditItem)
 		{
 			$buttonType=$this->getButtonType();
@@ -248,6 +247,8 @@ class TButtonColumn extends TDataGridColumn
 			$cell->getControls()->add($button);
 			$cell->registerObject('Button',$button);
 		}
+		else
+			parent::initializeCell($cell,$columnIndex,$itemType);
 	}
 
 	/**
@@ -261,13 +262,13 @@ class TButtonColumn extends TDataGridColumn
 		{
 			if(($field=$this->getDataTextField())!=='')
 			{
-				$value=$this->getDataFieldValue($sender->getNamingContainer()->getDataItem(),$field);
+				$value=$this->getDataFieldValue($sender->getNamingContainer()->getData(),$field);
 				$text=$this->formatDataValue($this->getDataTextFormatString(),$value);
 				$sender->setText($text);
 			}
 			if(($sender instanceof TImageButton) && ($field=$this->getDataImageUrlField())!=='')
 			{
-				$value=$this->getDataFieldValue($sender->getNamingContainer()->getDataItem(),$field);
+				$value=$this->getDataFieldValue($sender->getNamingContainer()->getData(),$field);
 				$url=$this->formatDataValue($this->getDataImageUrlFormatString(),$value);
 				$sender->setImageUrl($url);
 			}
diff --git a/framework/Web/UI/WebControls/TCheckBoxColumn.php b/framework/Web/UI/WebControls/TCheckBoxColumn.php
index beef3aa1..c70a67c9 100644
--- a/framework/Web/UI/WebControls/TCheckBoxColumn.php
+++ b/framework/Web/UI/WebControls/TCheckBoxColumn.php
@@ -88,7 +88,6 @@ class TCheckBoxColumn extends TDataGridColumn
 	 */
 	public function initializeCell($cell,$columnIndex,$itemType)
 	{
-		parent::initializeCell($cell,$columnIndex,$itemType);
 		if($itemType===TListItemType::Item || $itemType===TListItemType::AlternatingItem || $itemType===TListItemType::SelectedItem || $itemType===TListItemType::EditItem)
 		{
 			$checkBox=new TCheckBox;
@@ -100,6 +99,8 @@ class TCheckBoxColumn extends TDataGridColumn
 			if($this->getDataField()!=='')
 				$checkBox->attachEventHandler('OnDataBinding',array($this,'dataBindColumn'));
 		}
+		else
+			parent::initializeCell($cell,$columnIndex,$itemType);
 	}
 
 	/**
@@ -110,7 +111,7 @@ class TCheckBoxColumn extends TDataGridColumn
 	public function dataBindColumn($sender,$param)
 	{
 		$item=$sender->getNamingContainer();
-		$data=$item->getDataItem();
+		$data=$item->getData();
 		if(($field=$this->getDataField())!=='')
 			$value=TPropertyValue::ensureBoolean($this->getDataFieldValue($data,$field));
 		else
diff --git a/framework/Web/UI/WebControls/TDataGrid.php b/framework/Web/UI/WebControls/TDataGrid.php
index 90042b12..65717d71 100644
--- a/framework/Web/UI/WebControls/TDataGrid.php
+++ b/framework/Web/UI/WebControls/TDataGrid.php
@@ -1073,8 +1073,8 @@ class TDataGrid extends TBaseDataList implements INamingContainer
 				$cell=new TTableCell;
 			if(($id=$column->getID())!=='')
 				$item->registerObject($id,$cell);
-			$column->initializeCell($cell,$index,$itemType);
 			$cells->add($cell);
+			$column->initializeCell($cell,$index,$itemType);
 			$index++;
 		}
 	}
@@ -1777,7 +1777,7 @@ class TDataGridItem extends TTableRow implements INamingContainer
 	 * value of the data item
 	 * @var mixed
 	 */
-	private $_dataItem=null;
+	private $_data=null;
 
 	/**
 	 * Constructor.
@@ -1829,18 +1829,40 @@ class TDataGridItem extends TTableRow implements INamingContainer
 
 	/**
 	 * @return mixed data associated with the item
+	 * @since 3.1.0
+	 */
+	public function getData()
+	{
+		return $this->_data;
+	}
+
+	/**
+	 * @param mixed data to be associated with the item
+	 * @since 3.1.0
+	 */
+	public function setData($value)
+	{
+		$this->_data=$value;
+	}
+
+	/**
+	 * This property is deprecated since v3.1.0.
+	 * @return mixed data associated with the item
+	 * @deprecated deprecated since v3.1.0. Use {@link getData} instead.
 	 */
 	public function getDataItem()
 	{
-		return $this->_dataItem;
+		return $this->getData();
 	}
 
 	/**
+	 * This property is deprecated since v3.1.0.
 	 * @param mixed data to be associated with the item
+	 * @deprecated deprecated since version 3.1.0. Use {@link setData} instead.
 	 */
 	public function setDataItem($value)
 	{
-		$this->_dataItem=$value;
+		return $this->setData($value);
 	}
 
 	/**
diff --git a/framework/Web/UI/WebControls/TDataGridColumn.php b/framework/Web/UI/WebControls/TDataGridColumn.php
index f0d088d2..7228dcfd 100644
--- a/framework/Web/UI/WebControls/TDataGridColumn.php
+++ b/framework/Web/UI/WebControls/TDataGridColumn.php
@@ -10,10 +10,8 @@
  * @package System.Web.UI.WebControls
  */
 
-/**
- * Includes TDataFieldAccessor and TDataValueFormatter classes
- */
 Prado::using('System.Util.TDataFieldAccessor');
+Prado::using('System.Web.UI.WebControls.TDataGrid');
 
 /**
  * TDataGridColumn class
@@ -109,6 +107,28 @@ abstract class TDataGridColumn extends TApplicationComponent
 		$this->setViewState('HeaderImageUrl',$value,'');
 	}
 
+	/**
+	 * @return string the class name for the column header cell renderer. Defaults to empty, meaning not set.
+	 * @since 3.1.0
+	 */
+	public function getHeaderRenderer()
+	{
+		return $this->getViewState('HeaderRenderer','');
+	}
+
+	/**
+	 * Sets the column header cell renderer class.
+	 *
+	 * If not empty, the class will be used to instantiate as a child control in the column header cell.
+	 *
+	 * @param string the renderer class name in namespace format.
+	 * @since 3.1.0
+	 */
+	public function setHeaderRenderer($value)
+	{
+		$this->setViewState('HeaderRenderer',$value,'');
+	}
+
 	/**
 	 * @param boolean whether to create a style if previously not existing
 	 * @return TTableItemStyle the style for header
@@ -139,6 +159,28 @@ abstract class TDataGridColumn extends TApplicationComponent
 		$this->setViewState('FooterText',$value,'');
 	}
 
+	/**
+	 * @return string the class name for the column footer cell renderer. Defaults to empty, meaning not set.
+	 * @since 3.1.0
+	 */
+	public function getFooterRenderer()
+	{
+		return $this->getViewState('FooterRenderer','');
+	}
+
+	/**
+	 * Sets the column footer cell renderer class.
+	 *
+	 * If not empty, the class will be used to instantiate as a child control in the column footer cell.
+	 *
+	 * @param string the renderer class name in namespace format.
+	 * @since 3.1.0
+	 */
+	public function setFooterRenderer($value)
+	{
+		$this->setViewState('FooterRenderer',$value,'');
+	}
+
 	/**
 	 * @param boolean whether to create a style if previously not existing
 	 * @return TTableItemStyle the style for footer
@@ -301,55 +343,131 @@ abstract class TDataGridColumn extends TApplicationComponent
 	 */
 	public function initializeCell($cell,$columnIndex,$itemType)
 	{
-		switch($itemType)
+		if($itemType===TListItemType::Header)
+			$this->initializeHeaderCell($cell,$columnIndex);
+		else if($itemType===TListItemType::Footer)
+			$this->initializeFooterCell($cell,$columnIndex);
+	}
+
+	/**
+	 * Returns a value indicating whether this column allows sorting.
+	 * The column allows sorting only when {@link getSortExpression SortExpression}
+	 * is not empty and the datagrid allows sorting.
+	 * @return boolean whether this column allows sorting
+	 */
+	public function getAllowSorting()
+	{
+		return $this->getSortExpression()!=='' && (!$this->_owner || $this->_owner->getAllowSorting());
+	}
+
+	/**
+	 * Initializes the header cell.
+	 *
+	 * This method attempts to use {@link getHeaderRenderer HeaderRenderer} to
+	 * instantiate the header cell. If that is not available, it will populate
+	 * the cell with an image or a text string, depending on {@link getHeaderImageUrl HeaderImageUrl}
+	 * and {@link getHeaderText HeaderText} property values.
+	 *
+	 * If the column allows sorting, image or text will be created as
+	 * a button which issues <b>Sort</b> command upon user click.
+	 *
+	 * @param TTableCell the cell to be initialized
+	 * @param integer the index to the Columns property that the cell resides in.
+	 */
+	protected function initializeHeaderCell($cell,$columnIndex)
+	{
+		$text=$this->getHeaderText();
+
+		if(($classPath=$this->getHeaderRenderer())!=='')
 		{
-			case TDataGrid::IT_HEADER:
-				$sortExpression=$this->getSortExpression();
-				$allowSorting=$sortExpression!=='' && (!$this->_owner || $this->_owner->getAllowSorting());
-				if($allowSorting)
+			$control=Prado::createComponent($classPath);
+			if($control instanceof IDataRenderer)
+			{
+				if($control instanceof IItemDataRenderer)
 				{
-					if(($url=$this->getHeaderImageUrl())!=='')
-					{
-						$button=Prado::createComponent('System.Web.UI.WebControls.TImageButton');
-						$button->setImageUrl($url);
-						$button->setCommandName('Sort');
-						$button->setCommandParameter($sortExpression);
-						$button->setCausesValidation(false);
-						$cell->getControls()->add($button);
-					}
-					else if(($text=$this->getHeaderText())!=='')
-					{
-						$button=Prado::createComponent('System.Web.UI.WebControls.TLinkButton');
-						$button->setText($text);
-						$button->setCommandName('Sort');
-						$button->setCommandParameter($sortExpression);
-						$button->setCausesValidation(false);
-						$cell->getControls()->add($button);
-					}
-					else
-						$cell->setText('&nbsp;');
+					$item=$cell->getParent();
+					$control->setItemIndex($item->getItemIndex());
+					$control->setItemType($item->getItemType());
 				}
-				else
+				$control->setData($text);
+			}
+			$cell->getControls()->add($control);
+		}
+		else if($this->getAllowSorting())
+		{
+			$sortExpression=$this->getSortExpression();
+			if(($url=$this->getHeaderImageUrl())!=='')
+			{
+				$button=Prado::createComponent('System.Web.UI.WebControls.TImageButton');
+				$button->setImageUrl($url);
+				$button->setCommandName(TDataGrid::CMD_SORT);
+				$button->setCommandParameter($sortExpression);
+				if($text!=='')
+					$button->setAlternateText($text);
+				$button->setCausesValidation(false);
+				$cell->getControls()->add($button);
+			}
+			else if($text!=='')
+			{
+				$button=Prado::createComponent('System.Web.UI.WebControls.TLinkButton');
+				$button->setText($text);
+				$button->setCommandName(TDataGrid::CMD_SORT);
+				$button->setCommandParameter($sortExpression);
+				$button->setCausesValidation(false);
+				$cell->getControls()->add($button);
+			}
+			else
+				$cell->setText('&nbsp;');
+		}
+		else
+		{
+			if(($url=$this->getHeaderImageUrl())!=='')
+			{
+				$image=Prado::createComponent('System.Web.UI.WebControls.TImage');
+				$image->setImageUrl($url);
+				if($text!=='')
+					$image->setAlternateText($text);
+				$cell->getControls()->add($image);
+			}
+			else if($text!=='')
+				$cell->setText($text);
+			else
+				$cell->setText('&nbsp;');
+		}
+	}
+
+	/**
+	 * Initializes the footer cell.
+	 *
+	 * This method attempts to use {@link getFooterRenderer FooterRenderer} to
+	 * instantiate the footer cell. If that is not available, it will populate
+	 * the cell with a text string specified by {@link getFooterImageUrl FooterImageUrl}
+	 *
+	 * @param TTableCell the cell to be initialized
+	 * @param integer the index to the Columns property that the cell resides in.
+	 */
+	protected function initializeFooterCell($cell,$columnIndex)
+	{
+		$text=$this->getFooterText();
+		if(($classPath=$this->getFooterRenderer())!=='')
+		{
+			$control=Prado::createComponent($classPath);
+			if($control instanceof IDataRenderer)
+			{
+				if($control instanceof IItemDataRenderer)
 				{
-					if(($url=$this->getHeaderImageUrl())!=='')
-					{
-						$image=Prado::createComponent('System.Web.UI.WebControls.TImage');
-						$image->setImageUrl($url);
-						$cell->getControls()->add($image);
-					}
-					else
-					{
-						if(($text=$this->getHeaderText())==='')
-							$text='&nbsp;';
-						$cell->setText($text);
-					}
+					$item=$cell->getParent();
+					$control->setItemIndex($item->getItemIndex());
+					$control->setItemType($item->getItemType());
 				}
-				break;
-			case TDataGrid::IT_FOOTER:
-				if(($text=$this->getFooterText())!=='')
-					$cell->setText($text);
-				break;
+				$control->setData($text);
+			}
+			$cell->getControls()->add($control);
 		}
+		else if($text!=='')
+			$cell->setText($text);
+		else
+			$cell->setText('&nbsp;');
 	}
 
 	/**
diff --git a/framework/Web/UI/WebControls/TDataGridItemRenderer.php b/framework/Web/UI/WebControls/TDataGridItemRenderer.php
new file mode 100644
index 00000000..24b080c1
--- /dev/null
+++ b/framework/Web/UI/WebControls/TDataGridItemRenderer.php
@@ -0,0 +1,105 @@
+<?php
+/**
+ * TDataGridItemRenderer class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright &copy; 2007 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Web.UI.WebControls
+ */
+
+Prado::using('System.Web.UI.WebControls.TDataGrid');
+
+/**
+ * TDataGridItemRenderer class
+ *
+ * TDataGridItemRenderer can be used as a convenient base class to
+ * define an item renderer class for {@link TDataGrid}.
+ *
+ * Because TDataGridItemRenderer extends from {@link TTemplateControl}, derived child classes
+ * can have templates to define their presentational layout.
+ *
+ * TDataGridItemRenderer implements {@link IItemDataRenderer} interface,
+ * which enables the following properties that are related with data-bound controls:
+ * - {@link getItemIndex ItemIndex}: zero-based index of the datagrid item containing this control.
+ * - {@link getItemType ItemType}: type of the datagrid item containing this control, such as TListItemType::AlternatingItem
+ * - {@link getData Data}: the data row associated with the datagrid item that this control resides in
+
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @version $Id$
+ * @package System.Web.UI.WebControls
+ * @since 3.1.0
+ */
+class TDataGridItemRenderer extends TTemplateControl implements IItemDataRenderer
+{
+	/**
+	 * index of the data item in the Items collection of datalist
+	 */
+	private $_itemIndex;
+	/**
+	 * type of the TDataGridItem
+	 * @var TListItemType
+	 */
+	private $_itemType;
+	/**
+	 * data associated with this item
+	 * @var mixed
+	 */
+	private $_data;
+
+	/**
+	 * @return TListItemType item type
+	 */
+	public function getItemType()
+	{
+		return $this->_itemType;
+	}
+
+	/**
+	 * @param TListItemType item type.
+	 */
+	public function setItemType($value)
+	{
+		$this->_itemType=TPropertyValue::ensureEnum($value,'TListItemType');
+	}
+
+	/**
+	 * Returns a value indicating the zero-based index of the item in the corresponding data control's item collection.
+	 * If the item is not in the collection (e.g. it is a header item), it returns -1.
+	 * @return integer zero-based index of the item.
+	 */
+	public function getItemIndex()
+	{
+		return $this->_itemIndex;
+	}
+
+	/**
+	 * Sets the zero-based index for the item.
+	 * If the item is not in the item collection (e.g. it is a header item), -1 should be used.
+	 * @param integer zero-based index of the item.
+	 */
+	public function setItemIndex($value)
+	{
+		$this->_itemIndex=TPropertyValue::ensureInteger($value);
+	}
+
+	/**
+	 * @return mixed the data row associated with the datagrid item
+	 */
+	public function getData()
+	{
+		return $this->_data;
+	}
+
+	/**
+	 * @param mixed the data row to be associated with the datagrid item
+	 */
+	public function setData($value)
+	{
+		$this->_data=$value;
+	}
+}
+
+?>
\ No newline at end of file
diff --git a/framework/Web/UI/WebControls/TDataList.php b/framework/Web/UI/WebControls/TDataList.php
index 6538d7fa..25b1221f 100644
--- a/framework/Web/UI/WebControls/TDataList.php
+++ b/framework/Web/UI/WebControls/TDataList.php
@@ -205,6 +205,7 @@ class TDataList extends TBaseDataList implements INamingContainer, IRepeatInfoUs
 
 	/**
 	 * @return string the class name for datalist items. Defaults to empty, meaning not set.
+	 * @since 3.1.0
 	 */
 	public function getItemRenderer()
 	{
@@ -219,6 +220,7 @@ class TDataList extends TBaseDataList implements INamingContainer, IRepeatInfoUs
 	 *
 	 * @param string the renderer class name in namespace format.
 	 * @see setItemTemplate
+	 * @since 3.1.0
 	 */
 	public function setItemRenderer($value)
 	{
@@ -227,6 +229,7 @@ class TDataList extends TBaseDataList implements INamingContainer, IRepeatInfoUs
 
 	/**
 	 * @return string the class name for alternative datalist items. Defaults to empty, meaning not set.
+	 * @since 3.1.0
 	 */
 	public function getAlternatingItemRenderer()
 	{
@@ -241,6 +244,7 @@ class TDataList extends TBaseDataList implements INamingContainer, IRepeatInfoUs
 	 *
 	 * @param string the renderer class name in namespace format.
 	 * @see setAlternatingItemTemplate
+	 * @since 3.1.0
 	 */
 	public function setAlternatingItemRenderer($value)
 	{
@@ -249,6 +253,7 @@ class TDataList extends TBaseDataList implements INamingContainer, IRepeatInfoUs
 
 	/**
 	 * @return string the class name for the datalist item being editted. Defaults to empty, meaning not set.
+	 * @since 3.1.0
 	 */
 	public function getEditItemRenderer()
 	{
@@ -263,6 +268,7 @@ class TDataList extends TBaseDataList implements INamingContainer, IRepeatInfoUs
 	 *
 	 * @param string the renderer class name in namespace format.
 	 * @see setEditItemTemplate
+	 * @since 3.1.0
 	 */
 	public function setEditItemRenderer($value)
 	{
@@ -271,6 +277,7 @@ class TDataList extends TBaseDataList implements INamingContainer, IRepeatInfoUs
 
 	/**
 	 * @return string the class name for the datalist item being selected. Defaults to empty, meaning not set.
+	 * @since 3.1.0
 	 */
 	public function getSelectedItemRenderer()
 	{
@@ -285,6 +292,7 @@ class TDataList extends TBaseDataList implements INamingContainer, IRepeatInfoUs
 	 *
 	 * @param string the renderer class name in namespace format.
 	 * @see setSelectedItemTemplate
+	 * @since 3.1.0
 	 */
 	public function setSelectedItemRenderer($value)
 	{
@@ -293,6 +301,7 @@ class TDataList extends TBaseDataList implements INamingContainer, IRepeatInfoUs
 
 	/**
 	 * @return string the class name for datalist item separators. Defaults to empty, meaning not set.
+	 * @since 3.1.0
 	 */
 	public function getSeparatorRenderer()
 	{
@@ -307,6 +316,7 @@ class TDataList extends TBaseDataList implements INamingContainer, IRepeatInfoUs
 	 *
 	 * @param string the renderer class name in namespace format.
 	 * @see setSeparatorTemplate
+	 * @since 3.1.0
 	 */
 	public function setSeparatorRenderer($value)
 	{
@@ -315,6 +325,7 @@ class TDataList extends TBaseDataList implements INamingContainer, IRepeatInfoUs
 
 	/**
 	 * @return string the class name for datalist header item. Defaults to empty, meaning not set.
+	 * @since 3.1.0
 	 */
 	public function getHeaderRenderer()
 	{
@@ -329,6 +340,7 @@ class TDataList extends TBaseDataList implements INamingContainer, IRepeatInfoUs
 	 *
 	 * @param string the renderer class name in namespace format.
 	 * @see setHeaderTemplate
+	 * @since 3.1.0
 	 */
 	public function setHeaderRenderer($value)
 	{
@@ -337,6 +349,7 @@ class TDataList extends TBaseDataList implements INamingContainer, IRepeatInfoUs
 
 	/**
 	 * @return string the class name for datalist footer item. Defaults to empty, meaning not set.
+	 * @since 3.1.0
 	 */
 	public function getFooterRenderer()
 	{
@@ -351,6 +364,7 @@ class TDataList extends TBaseDataList implements INamingContainer, IRepeatInfoUs
 	 *
 	 * @param string the renderer class name in namespace format.
 	 * @see setFooterTemplate
+	 * @since 3.1.0
 	 */
 	public function setFooterRenderer($value)
 	{
@@ -359,6 +373,7 @@ class TDataList extends TBaseDataList implements INamingContainer, IRepeatInfoUs
 
 	/**
 	 * @return string the class name for empty datalist item. Defaults to empty, meaning not set.
+	 * @since 3.1.0
 	 */
 	public function getEmptyRenderer()
 	{
@@ -374,6 +389,7 @@ class TDataList extends TBaseDataList implements INamingContainer, IRepeatInfoUs
 	 *
 	 * @param string the renderer class name in namespace format.
 	 * @see setEmptyTemplate
+	 * @since 3.1.0
 	 */
 	public function setEmptyRenderer($value)
 	{
@@ -1638,6 +1654,7 @@ class TDataListItem extends TWebControl implements INamingContainer, IItemDataRe
 
 	/**
 	 * @return mixed data associated with the item
+	 * @since 3.1.0
 	 */
 	public function getData()
 	{
@@ -1646,6 +1663,7 @@ class TDataListItem extends TWebControl implements INamingContainer, IItemDataRe
 
 	/**
 	 * @param mixed data to be associated with the item
+	 * @since 3.1.0
 	 */
 	public function setData($value)
 	{
diff --git a/framework/Web/UI/WebControls/TDropDownListColumn.php b/framework/Web/UI/WebControls/TDropDownListColumn.php
index 384a3701..c3428371 100644
--- a/framework/Web/UI/WebControls/TDropDownListColumn.php
+++ b/framework/Web/UI/WebControls/TDropDownListColumn.php
@@ -255,7 +255,6 @@ class TDropDownListColumn extends TDataGridColumn
 	 */
 	public function initializeCell($cell,$columnIndex,$itemType)
 	{
-		parent::initializeCell($cell,$columnIndex,$itemType);
 		if(!$this->_dataBound && $this->_listControl->getDataSource()!==null)
 		{
 			$this->_listControl->setDataTextField($this->getListTextField());
@@ -284,6 +283,9 @@ class TDropDownListColumn extends TDataGridColumn
 				if($this->getDataTextField()!=='' || $this->getDataValueField()!=='')
 					$cell->attachEventHandler('OnDataBinding',array($this,'dataBindColumn'));
 				break;
+			default:
+				parent::initializeCell($cell,$columnIndex,$itemType);
+				break;
 		}
 	}
 
@@ -295,7 +297,7 @@ class TDropDownListColumn extends TDataGridColumn
 	public function dataBindColumn($sender,$param)
 	{
 		$item=$sender->getNamingContainer();
-		$data=$item->getDataItem();
+		$data=$item->getData();
 		if(($valueField=$this->getDataValueField())!=='')
 			$value=$this->getDataFieldValue($data,$valueField);
 		else
diff --git a/framework/Web/UI/WebControls/TEditCommandColumn.php b/framework/Web/UI/WebControls/TEditCommandColumn.php
index 74e92852..9212e7fd 100644
--- a/framework/Web/UI/WebControls/TEditCommandColumn.php
+++ b/framework/Web/UI/WebControls/TEditCommandColumn.php
@@ -208,7 +208,6 @@ class TEditCommandColumn extends TDataGridColumn
 	 */
 	public function initializeCell($cell,$columnIndex,$itemType)
 	{
-		parent::initializeCell($cell,$columnIndex,$itemType);
 		if($itemType===TListItemType::Item || $itemType===TListItemType::AlternatingItem || $itemType===TListItemType::SelectedItem)
 		{
 			$button=$this->createButton('Edit',$this->getEditText(),false,'');
@@ -226,6 +225,8 @@ class TEditCommandColumn extends TDataGridColumn
 			$controls->add($button);
 			$cell->registerObject('CancelButton',$button);
 		}
+		else
+			parent::initializeCell($cell,$columnIndex,$itemType);
 	}
 
 	/**
diff --git a/framework/Web/UI/WebControls/THyperLinkColumn.php b/framework/Web/UI/WebControls/THyperLinkColumn.php
index 9b20bbfc..f0f0b765 100644
--- a/framework/Web/UI/WebControls/THyperLinkColumn.php
+++ b/framework/Web/UI/WebControls/THyperLinkColumn.php
@@ -176,7 +176,6 @@ class THyperLinkColumn extends TDataGridColumn
 	 */
 	public function initializeCell($cell,$columnIndex,$itemType)
 	{
-		parent::initializeCell($cell,$columnIndex,$itemType);
 		if($itemType===TListItemType::Item || $itemType===TListItemType::AlternatingItem || $itemType===TListItemType::SelectedItem || $itemType===TListItemType::EditItem)
 		{
 			$link=new THyperLink;
@@ -188,6 +187,8 @@ class THyperLinkColumn extends TDataGridColumn
 			$cell->getControls()->add($link);
 			$cell->registerObject('HyperLink',$link);
 		}
+		else
+			parent::initializeCell($cell,$columnIndex,$itemType);
 	}
 
 	/**
@@ -198,7 +199,7 @@ class THyperLinkColumn extends TDataGridColumn
 	public function dataBindColumn($sender,$param)
 	{
 		$item=$sender->getNamingContainer();
-		$data=$item->getDataItem();
+		$data=$item->getData();
 		if(($field=$this->getDataTextField())!=='')
 		{
 			$value=$this->getDataFieldValue($data,$field);
diff --git a/framework/Web/UI/WebControls/TLiteralColumn.php b/framework/Web/UI/WebControls/TLiteralColumn.php
index e78c4e6d..6a1f3466 100644
--- a/framework/Web/UI/WebControls/TLiteralColumn.php
+++ b/framework/Web/UI/WebControls/TLiteralColumn.php
@@ -108,7 +108,6 @@ class TLiteralColumn extends TDataGridColumn
 	 */
 	public function initializeCell($cell,$columnIndex,$itemType)
 	{
-		parent::initializeCell($cell,$columnIndex,$itemType);
 		if($itemType===TListItemType::Item || $itemType===TListItemType::AlternatingItem || $itemType===TListItemType::EditItem || $itemType===TListItemType::SelectedItem)
 		{
 			if($this->getDataField()!=='')
@@ -126,6 +125,8 @@ class TLiteralColumn extends TDataGridColumn
 				}
 			}
 		}
+		else
+			parent::initializeCell($cell,$columnIndex,$itemType);
 	}
 
 	/**
@@ -136,7 +137,7 @@ class TLiteralColumn extends TDataGridColumn
 	public function dataBindColumn($sender,$param)
 	{
 		$item=$sender->getNamingContainer();
-		$data=$item->getDataItem();
+		$data=$item->getData();
 		$formatString=$this->getDataFormatString();
 		if(($field=$this->getDataField())!=='')
 			$value=$this->formatDataValue($formatString,$this->getDataFieldValue($data,$field));
diff --git a/framework/Web/UI/WebControls/TRepeater.php b/framework/Web/UI/WebControls/TRepeater.php
index 3d5b194b..c94d559a 100644
--- a/framework/Web/UI/WebControls/TRepeater.php
+++ b/framework/Web/UI/WebControls/TRepeater.php
@@ -143,6 +143,7 @@ class TRepeater extends TDataBoundControl implements INamingContainer
 
 	/**
 	 * @return string the class name for repeater items. Defaults to empty, meaning not set.
+	 * @since 3.1.0
 	 */
 	public function getItemRenderer()
 	{
@@ -157,6 +158,7 @@ class TRepeater extends TDataBoundControl implements INamingContainer
 	 *
 	 * @param string the renderer class name in namespace format.
 	 * @see setItemTemplate
+	 * @since 3.1.0
 	 */
 	public function setItemRenderer($value)
 	{
@@ -165,6 +167,7 @@ class TRepeater extends TDataBoundControl implements INamingContainer
 
 	/**
 	 * @return string the class name for alternative repeater items. Defaults to empty, meaning not set.
+	 * @since 3.1.0
 	 */
 	public function getAlternatingItemRenderer()
 	{
@@ -179,6 +182,7 @@ class TRepeater extends TDataBoundControl implements INamingContainer
 	 *
 	 * @param string the renderer class name in namespace format.
 	 * @see setAlternatingItemTemplate
+	 * @since 3.1.0
 	 */
 	public function setAlternatingItemRenderer($value)
 	{
@@ -187,6 +191,7 @@ class TRepeater extends TDataBoundControl implements INamingContainer
 
 	/**
 	 * @return string the class name for repeater item separators. Defaults to empty, meaning not set.
+	 * @since 3.1.0
 	 */
 	public function getSeparatorRenderer()
 	{
@@ -201,6 +206,7 @@ class TRepeater extends TDataBoundControl implements INamingContainer
 	 *
 	 * @param string the renderer class name in namespace format.
 	 * @see setSeparatorTemplate
+	 * @since 3.1.0
 	 */
 	public function setSeparatorRenderer($value)
 	{
@@ -209,6 +215,7 @@ class TRepeater extends TDataBoundControl implements INamingContainer
 
 	/**
 	 * @return string the class name for repeater header item. Defaults to empty, meaning not set.
+	 * @since 3.1.0
 	 */
 	public function getHeaderRenderer()
 	{
@@ -223,6 +230,7 @@ class TRepeater extends TDataBoundControl implements INamingContainer
 	 *
 	 * @param string the renderer class name in namespace format.
 	 * @see setHeaderTemplate
+	 * @since 3.1.0
 	 */
 	public function setHeaderRenderer($value)
 	{
@@ -231,6 +239,7 @@ class TRepeater extends TDataBoundControl implements INamingContainer
 
 	/**
 	 * @return string the class name for repeater footer item. Defaults to empty, meaning not set.
+	 * @since 3.1.0
 	 */
 	public function getFooterRenderer()
 	{
@@ -245,6 +254,7 @@ class TRepeater extends TDataBoundControl implements INamingContainer
 	 *
 	 * @param string the renderer class name in namespace format.
 	 * @see setFooterTemplate
+	 * @since 3.1.0
 	 */
 	public function setFooterRenderer($value)
 	{
@@ -253,6 +263,7 @@ class TRepeater extends TDataBoundControl implements INamingContainer
 
 	/**
 	 * @return string the class name for empty repeater item. Defaults to empty, meaning not set.
+	 * @since 3.1.0
 	 */
 	public function getEmptyRenderer()
 	{
@@ -268,6 +279,7 @@ class TRepeater extends TDataBoundControl implements INamingContainer
 	 *
 	 * @param string the renderer class name in namespace format.
 	 * @see setEmptyTemplate
+	 * @since 3.1.0
 	 */
 	public function setEmptyRenderer($value)
 	{
@@ -925,6 +937,7 @@ class TRepeaterItem extends TControl implements INamingContainer, IItemDataRende
 
 	/**
 	 * @return mixed data associated with the item
+	 * @since 3.1.0
 	 */
 	public function getData()
 	{
@@ -933,6 +946,7 @@ class TRepeaterItem extends TControl implements INamingContainer, IItemDataRende
 
 	/**
 	 * @param mixed data to be associated with the item
+	 * @since 3.1.0
 	 */
 	public function setData($value)
 	{
diff --git a/framework/Web/UI/WebControls/TTemplateColumn.php b/framework/Web/UI/WebControls/TTemplateColumn.php
index 7235fc8c..fe9674cc 100644
--- a/framework/Web/UI/WebControls/TTemplateColumn.php
+++ b/framework/Web/UI/WebControls/TTemplateColumn.php
@@ -43,6 +43,50 @@ class TTemplateColumn extends TDataGridColumn
 	private $_headerTemplate=null;
 	private $_footerTemplate=null;
 
+	/**
+	 * @return string the class name for the item cell renderer. Defaults to empty, meaning not set.
+	 * @since 3.1.0
+	 */
+	public function getItemRenderer()
+	{
+		return $this->getViewState('ItemRenderer','');
+	}
+
+	/**
+	 * Sets the item cell renderer class.
+	 *
+	 * If not empty, the class will be used to instantiate as a child control in the item cells of the column.
+	 *
+	 * @param string the renderer class name in namespace format.
+	 * @since 3.1.0
+	 */
+	public function setItemRenderer($value)
+	{
+		$this->setViewState('ItemRenderer',$value,'');
+	}
+
+	/**
+	 * @return string the class name for the edit item cell renderer. Defaults to empty, meaning not set.
+	 * @since 3.1.0
+	 */
+	public function getEditItemRenderer()
+	{
+		return $this->getViewState('EditItemRenderer','');
+	}
+
+	/**
+	 * Sets the edit item cell renderer class.
+	 *
+	 * If not empty, the class will be used to instantiate as a child control in the item cell that is in edit mode.
+	 *
+	 * @param string the renderer class name in namespace format.
+	 * @since 3.1.0
+	 */
+	public function setEditItemRenderer($value)
+	{
+		$this->setViewState('EditItemRenderer',$value,'');
+	}
+
 	/**
 	 * @return ITemplate the edit item template
 	 */
@@ -134,33 +178,65 @@ class TTemplateColumn extends TDataGridColumn
 	 */
 	public function initializeCell($cell,$columnIndex,$itemType)
 	{
-		parent::initializeCell($cell,$columnIndex,$itemType);
-		$template=null;
-		switch($itemType)
+		if($itemType===TListItemType::Item || $itemType===TListItemType::AlternatingItem || $itemType===TListItemType::SelectedItem || $itemType===TListItemType::EditItem)
 		{
-			case TListItemType::Header:
-				$template=$this->_headerTemplate;
-				break;
-			case TListItemType::Footer:
-				$template=$this->_footerTemplate;
-				break;
-			case TListItemType::Item:
-			case TListItemType::AlternatingItem:
-			case TListItemType::SelectedItem:
-				$template=$this->_itemTemplate;
-				break;
-			case TListItemType::EditItem:
+			if($itemType===TListItemType::EditItem)
+			{
 				$template=$this->_editItemTemplate===null?$this->_itemTemplate:$this->_editItemTemplate;
-				break;
+				if(($classPath=$this->getEditItemRenderer())==='')
+					$classPath=$this->getItemRenderer();
+			}
+			else
+			{
+				$template=$this->_itemTemplate;
+				$classPath=$this->getItemRenderer();
+			}
+			if($classPath!=='')
+			{
+				$control=Prado::createComponent($classPath);
+				$cell->getControls()->add($control);
+				if($control instanceof IItemDataRenderer)
+				{
+					$control->setItemIndex($cell->getParent()->getItemIndex());
+					$control->setItemType($itemType);
+				}
+				if($control instanceof IDataRenderer)
+					$control->attachEventHandler('OnDataBinding',array($this,'dataBindColumn'));
+			}
+			else if($template!==null)
+				$template->instantiateIn($cell);
+			else if($itemType!==TListItemType::EditItem)
+				$cell->setText('&nbsp;');
 		}
-		if($template!==null)
+		else if($itemType===TListItemType::Header)
 		{
-			$cell->setText('');
-			$cell->getControls()->clear();
-			$template->instantiateIn($cell);
+			if(($classPath=$this->getHeaderRenderer())!=='')
+				$this->initializeHeaderCell($cell,$columnIndex);
+			else if($this->_headerTemplate!==null)
+				$this->_headerTemplate->instantiateIn($cell);
+			else
+				$this->initializeHeaderCell($cell,$columnIndex);
 		}
-		else if($itemType===TListItemType::Item || $itemType===TListItemType::AlternatingItem || $itemType===TListItemType::SelectedItem || $itemType===TListItemType::EditItem)
-			$cell->setText('&nbsp;');
+		else if($itemType===TListItemType::Footer)
+		{
+			if(($classPath=$this->getFooterRenderer())!=='')
+				$this->initializeFooterCell($cell,$columnIndex);
+			else if($this->_footerTemplate!==null)
+				$this->_footerTemplate->instantiateIn($cell);
+			else
+				$this->initializeHeaderCell($cell,$columnIndex);
+		}
+	}
+
+	/**
+	 * Databinds a cell in the column.
+	 * This method is invoked when datagrid performs databinding.
+	 * It populates the content of the cell with the relevant data from data source.
+	 */
+	public function dataBindColumn($sender,$param)
+	{
+		$item=$sender->getNamingContainer();
+		$sender->setData($item->getData());
 	}
 }
 
-- 
cgit v1.2.3