Subversion-Projekte lars-tiefland.prado

Revision

Blame | Letzte Änderung | Log anzeigen | RSS feed

<?php
/**
 * TDataList class file
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @link http://www.pradosoft.com/
 * @copyright Copyright &copy; 2005-2008 PradoSoft
 * @license http://www.pradosoft.com/license/
 * @version $Id: TDataList.php 2541 2008-10-21 15:05:13Z qiang.xue $
 * @package System.Web.UI.WebControls
 */

/**
 * Includes TBaseDataList class
 */
Prado::using('System.Web.UI.WebControls.TBaseDataList');
/**
 * Includes TRepeatInfo class
 */
Prado::using('System.Web.UI.WebControls.TRepeatInfo');

/**
 * TDataList class
 *
 * TDataList represents a data bound and updatable list control.
 *
 * Like {@link TRepeater}, TDataList displays its content repeatedly based on
 * the data fetched from {@link setDataSource DataSource}.
 * The repeated contents in TDataList are called items, which are controls and
 * can be accessed through {@link getItems Items}. When {@link dataBind()} is
 * invoked, TDataList creates an item for each row of data and binds the data
 * row to the item. Optionally, a TDataList can have a header, a footer and/or
 * separators between items.
 *
 * TDataList differs from {@link TRepeater} in that it supports tiling the items
 * in different manners and it maintains status of items to handle data update.
 *
 * The layout of the repeated contents are specified by inline templates.
 * TDataList items, header, footer, etc. are being instantiated with the corresponding
 * templates when data is being bound to the repeater.
 *
 * Since v3.1.0, the layout can also be by renderers. A renderer is a control class
 * that can be instantiated as datalist items, header, etc. A renderer can thus be viewed
 * as an external template (in fact, it can also be non-templated controls).
 *
 * A renderer can be any control class.
 * - If the class implements {@link IDataRenderer}, the <b>Data</b>
 * property will be set as the data row during databinding. Many PRADO controls
 * implement this interface, such as {@link TLabel}, {@link TTextBox}, etc.
 * - If the class implements {@link IItemDataRenderer}, the <b>ItemIndex</b> property will be set
 * as the zero-based index of the item in the datalist item collection, and
 * the <b>ItemType</b> property as the item's type (such as TListItemType::Item).
 * {@link TDataListItemRenderer} may be used as the convenient base class which
 * already implements {@link IDataItemRenderer}.
 *
 * The following properties are used to specify different types of template and renderer
 * for a datalist:
 * - {@link setItemTemplate ItemTemplate}, {@link setItemRenderer ItemRenderer}:
 * for each repeated row of data
 * - {@link setAlternatingItemTemplate AlternatingItemTemplate}, {@link setAlternatingItemRenderer AlternatingItemRenderer}:
 * for each alternating row of data. If not set, {@link setItemTemplate ItemTemplate} or {@link setItemRenderer ItemRenderer}
 * will be used instead.
 * - {@link setHeaderTemplate HeaderTemplate}, {@link setHeaderRenderer HeaderRenderer}:
 * for the datalist header.
 * - {@link setFooterTemplate FooterTemplate}, {@link setFooterRenderer FooterRenderer}:
 * for the datalist footer.
 * - {@link setSeparatorTemplate SeparatorTemplate}, {@link setSeparatorRenderer SeparatorRenderer}:
 * for content to be displayed between items.
 * - {@link setEmptyTemplate EmptyTemplate}, {@link setEmptyRenderer EmptyRenderer}:
 * used when data bound to the datalist is empty.
 * - {@link setEditItemTemplate EditItemTemplate}, {@link setEditItemRenderer EditItemRenderer}:
 * for the row being editted.
 * - {@link setSelectedItemTemplate SelectedItemTemplate}, {@link setSelectedItemRenderer SelectedItemRenderer}:
 * for the row being selected.
 *
 * If a content type is defined with both a template and a renderer, the latter takes precedence.
 *
 * When {@link dataBind()} is being called, TDataList undergoes the following lifecycles for each row of data:
 * - create item based on templates or renderers
 * - set the row of data to the item
 * - raise {@link onItemCreated OnItemCreated}:
 * - add the item as a child control
 * - call dataBind() of the item
 * - raise {@link onItemDataBound OnItemDataBound}:
 *
 * TDataList raises an {@link onItemCommand OnItemCommand} whenever a button control
 * within some datalist item raises a <b>OnCommand</b> event. Therefore,
 * you can handle all sorts of <b>OnCommand</b> event in a central place by
 * writing an event handler for {@link onItemCommand OnItemCommand}.
 *
 * An additional event is raised if the <b>OnCommand</b> event has one of the following
 * command names:
 * - edit: user wants to edit an item. <b>OnEditCommand</b> event will be raised.
 * - update: user wants to save the change to an item. <b>OnUpdateCommand</b> event will be raised.
 * - select: user selects an item. <b>OnSelectedIndexChanged</b> event will be raised.
 * - delete: user deletes an item. <b>OnDeleteCommand</b> event will be raised.
 * - cancel: user cancels previously editting action. <b>OnCancelCommand</b> event will be raised.
 *
 * TDataList provides a few properties to support tiling the items.
 * The number of columns used to display the data items is specified via
 * {@link setRepeatColumns RepeatColumns} property, while the {@link setRepeatDirection RepeatDirection}
 * governs the order of the items being rendered.
 * The layout of the data items in the list is specified via {@link setRepeatLayout RepeatLayout},
 * which can take one of the following values:
 * - Table (default): items are organized using HTML table and cells.
 * When using this layout, one can set {@link setCellPadding CellPadding} and
 * {@link setCellSpacing CellSpacing} to adjust the cellpadding and cellpadding
 * of the table, and {@link setCaption Caption} and {@link setCaptionAlign CaptionAlign}
 * to add a table caption with the specified alignment.
 * - Flow: items are organized using HTML spans and breaks.
 * - Raw: TDataList does not generate any HTML tags to do the tiling.
 *
 * Items in TDataList can be in one of the three status: normal browsing,
 * being editted and being selected. To change the status of a particular
 * item, set {@link setSelectedItemIndex SelectedItemIndex} or
 * {@link setEditItemIndex EditItemIndex}. The former will change
 * the indicated item to selected mode, which will cause the item to
 * use {@link setSelectedItemTemplate SelectedItemTemplate} or
 * {@link setSelectedItemRenderer SelectedItemRenderer} for presentation.
 * The latter will change the indicated item to edit mode and to use corresponding
 * template or renderer.
 * Note, if an item is in edit mode, then selecting this item will have no effect.
 *
 * Different styles may be applied to items in different status. The style
 * application is performed in a hierarchical way: Style in higher hierarchy
 * will inherit from styles in lower hierarchy.
 * Starting from the lowest hierarchy, the item styles include
 * - item's own style
 * - {@link getItemStyle ItemStyle}
 * - {@link getAlternatingItemStyle AlternatingItemStyle}
 * - {@link getSelectedItemStyle SelectedItemStyle}
 * - {@link getEditItemStyle EditItemStyle}.
 * Therefore, if background color is set as red in {@link getItemStyle ItemStyle},
 * {@link getEditItemStyle EditItemStyle} will also have red background color
 * unless it is set to a different value explicitly.
 *
 * When a page containing a datalist is post back, the datalist will restore automatically
 * all its contents, including items, header, footer and separators.
 * However, the data row associated with each item will not be recovered and become null.
 * To access the data, use one of the following ways:
 * - Use {@link getDataKeys DataKeys} to obtain the data key associated with
 * the specified datalist item and use the key to fetch the corresponding data
 * from some persistent storage such as DB.
 * - Save the whole dataset in viewstate, which will restore the dataset automatically upon postback.
 * Be aware though, if the size of your dataset is big, your page size will become big. Some
 * complex data may also have serializing problem if saved in viewstate.
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @version $Id: TDataList.php 2541 2008-10-21 15:05:13Z qiang.xue $
 * @package System.Web.UI.WebControls
 * @since 3.0
 */
class TDataList extends TBaseDataList implements INamingContainer, IRepeatInfoUser
{
        /**
         * Command name that TDataList understands. They are case-insensitive.
         */
        const CMD_SELECT='Select';
        const CMD_EDIT='Edit';
        const CMD_UPDATE='Update';
        const CMD_DELETE='Delete';
        const CMD_CANCEL='Cancel';

        /**
         * @var TDataListItemCollection item list
         */
        private $_items=null;
        /**
         * @var Itemplate various item templates
         */
        private $_itemTemplate=null;
        private $_emptyTemplate=null;
        private $_alternatingItemTemplate=null;
        private $_selectedItemTemplate=null;
        private $_editItemTemplate=null;
        private $_headerTemplate=null;
        private $_footerTemplate=null;
        private $_separatorTemplate=null;
        /**
         * @var TControl header item
         */
        private $_header=null;
        /**
         * @var TControl footer item
         */
        private $_footer=null;

        /**
         * @return TDataListItemCollection item list
         */
        public function getItems()
        {
                if(!$this->_items)
                        $this->_items=new TDataListItemCollection;
                return $this->_items;
        }

        /**
         * @return integer number of items
         */
        public function getItemCount()
        {
                return $this->_items?$this->_items->getCount():0;
        }

        /**
         * @return string the class name for datalist items. Defaults to empty, meaning not set.
         * @since 3.1.0
         */
        public function getItemRenderer()
        {
                return $this->getViewState('ItemRenderer','');
        }

        /**
         * Sets the item renderer class.
         *
         * If not empty, the class will be used to instantiate as datalist items.
         * This property takes precedence over {@link getItemTemplate ItemTemplate}.
         *
         * @param string the renderer class name in namespace format.
         * @see setItemTemplate
         * @since 3.1.0
         */
        public function setItemRenderer($value)
        {
                $this->setViewState('ItemRenderer',$value,'');
        }

        /**
         * @return string the class name for alternative datalist items. Defaults to empty, meaning not set.
         * @since 3.1.0
         */
        public function getAlternatingItemRenderer()
        {
                return $this->getViewState('AlternatingItemRenderer','');
        }

        /**
         * Sets the alternative item renderer class.
         *
         * If not empty, the class will be used to instantiate as alternative datalist items.
         * This property takes precedence over {@link getAlternatingItemTemplate AlternatingItemTemplate}.
         *
         * @param string the renderer class name in namespace format.
         * @see setAlternatingItemTemplate
         * @since 3.1.0
         */
        public function setAlternatingItemRenderer($value)
        {
                $this->setViewState('AlternatingItemRenderer',$value,'');
        }

        /**
         * @return string the class name for the datalist item being editted. Defaults to empty, meaning not set.
         * @since 3.1.0
         */
        public function getEditItemRenderer()
        {
                return $this->getViewState('EditItemRenderer','');
        }

        /**
         * Sets the renderer class for the datalist item being editted.
         *
         * If not empty, the class will be used to instantiate as the datalist item.
         * This property takes precedence over {@link getEditItemTemplate EditItemTemplate}.
         *
         * @param string the renderer class name in namespace format.
         * @see setEditItemTemplate
         * @since 3.1.0
         */
        public function setEditItemRenderer($value)
        {
                $this->setViewState('EditItemRenderer',$value,'');
        }

        /**
         * @return string the class name for the datalist item being selected. Defaults to empty, meaning not set.
         * @since 3.1.0
         */
        public function getSelectedItemRenderer()
        {
                return $this->getViewState('SelectedItemRenderer','');
        }

        /**
         * Sets the renderer class for the datalist item being selected.
         *
         * If not empty, the class will be used to instantiate as the datalist item.
         * This property takes precedence over {@link getSelectedItemTemplate SelectedItemTemplate}.
         *
         * @param string the renderer class name in namespace format.
         * @see setSelectedItemTemplate
         * @since 3.1.0
         */
        public function setSelectedItemRenderer($value)
        {
                $this->setViewState('SelectedItemRenderer',$value,'');
        }

        /**
         * @return string the class name for datalist item separators. Defaults to empty, meaning not set.
         * @since 3.1.0
         */
        public function getSeparatorRenderer()
        {
                return $this->getViewState('SeparatorRenderer','');
        }

        /**
         * Sets the datalist item separator renderer class.
         *
         * If not empty, the class will be used to instantiate as datalist item separators.
         * This property takes precedence over {@link getSeparatorTemplate SeparatorTemplate}.
         *
         * @param string the renderer class name in namespace format.
         * @see setSeparatorTemplate
         * @since 3.1.0
         */
        public function setSeparatorRenderer($value)
        {
                $this->setViewState('SeparatorRenderer',$value,'');
        }

        /**
         * @return string the class name for datalist header item. Defaults to empty, meaning not set.
         * @since 3.1.0
         */
        public function getHeaderRenderer()
        {
                return $this->getViewState('HeaderRenderer','');
        }

        /**
         * Sets the datalist header renderer class.
         *
         * If not empty, the class will be used to instantiate as datalist header item.
         * This property takes precedence over {@link getHeaderTemplate HeaderTemplate}.
         *
         * @param string the renderer class name in namespace format.
         * @see setHeaderTemplate
         * @since 3.1.0
         */
        public function setHeaderRenderer($value)
        {
                $this->setViewState('HeaderRenderer',$value,'');
        }

        /**
         * @return string the class name for datalist footer item. Defaults to empty, meaning not set.
         * @since 3.1.0
         */
        public function getFooterRenderer()
        {
                return $this->getViewState('FooterRenderer','');
        }

        /**
         * Sets the datalist footer renderer class.
         *
         * If not empty, the class will be used to instantiate as datalist footer item.
         * This property takes precedence over {@link getFooterTemplate FooterTemplate}.
         *
         * @param string the renderer class name in namespace format.
         * @see setFooterTemplate
         * @since 3.1.0
         */
        public function setFooterRenderer($value)
        {
                $this->setViewState('FooterRenderer',$value,'');
        }

        /**
         * @return string the class name for empty datalist item. Defaults to empty, meaning not set.
         * @since 3.1.0
         */
        public function getEmptyRenderer()
        {
                return $this->getViewState('EmptyRenderer','');
        }

        /**
         * Sets the datalist empty renderer class.
         *
         * The empty renderer is created as the child of the datalist
         * if data bound to the datalist is empty.
         * This property takes precedence over {@link getEmptyTemplate EmptyTemplate}.
         *
         * @param string the renderer class name in namespace format.
         * @see setEmptyTemplate
         * @since 3.1.0
         */
        public function setEmptyRenderer($value)
        {
                $this->setViewState('EmptyRenderer',$value,'');
        }

        /**
         * @return ITemplate the template for item
         */
        public function getItemTemplate()
        {
                return $this->_itemTemplate;
        }

        /**
         * @param ITemplate the template for item
         * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null.
         */
        public function setItemTemplate($value)
        {
                if($value instanceof ITemplate || $value===null)
                        $this->_itemTemplate=$value;
                else
                        throw new TInvalidDataTypeException('datalist_template_required','ItemTemplate');
        }

        /**
         * @return TTableItemStyle the style for item
         */
        public function getItemStyle()
        {
                if(($style=$this->getViewState('ItemStyle',null))===null)
                {
                        $style=new TTableItemStyle;
                        $this->setViewState('ItemStyle',$style,null);
                }
                return $style;
        }

        /**
         * @return ITemplate the template for each alternating item
         */
        public function getAlternatingItemTemplate()
        {
                return $this->_alternatingItemTemplate;
        }

        /**
         * @param ITemplate the template for each alternating item
         * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null.
         */
        public function setAlternatingItemTemplate($value)
        {
                if($value instanceof ITemplate || $value===null)
                        $this->_alternatingItemTemplate=$value;
                else
                        throw new TInvalidDataTypeException('datalist_template_required','AlternatingItemType');
        }

        /**
         * @return TTableItemStyle the style for each alternating item
         */
        public function getAlternatingItemStyle()
        {
                if(($style=$this->getViewState('AlternatingItemStyle',null))===null)
                {
                        $style=new TTableItemStyle;
                        $this->setViewState('AlternatingItemStyle',$style,null);
                }
                return $style;
        }

        /**
         * @return ITemplate the selected item template
         */
        public function getSelectedItemTemplate()
        {
                return $this->_selectedItemTemplate;
        }

        /**
         * @param ITemplate the selected item template
         * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null.
         */
        public function setSelectedItemTemplate($value)
        {
                if($value instanceof ITemplate || $value===null)
                        $this->_selectedItemTemplate=$value;
                else
                        throw new TInvalidDataTypeException('datalist_template_required','SelectedItemTemplate');
        }

        /**
         * @return TTableItemStyle the style for selected item
         */
        public function getSelectedItemStyle()
        {
                if(($style=$this->getViewState('SelectedItemStyle',null))===null)
                {
                        $style=new TTableItemStyle;
                        $this->setViewState('SelectedItemStyle',$style,null);
                }
                return $style;
        }

        /**
         * @return ITemplate the edit item template
         */
        public function getEditItemTemplate()
        {
                return $this->_editItemTemplate;
        }

        /**
         * @param ITemplate the edit item template
         * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null.
         */
        public function setEditItemTemplate($value)
        {
                if($value instanceof ITemplate || $value===null)
                        $this->_editItemTemplate=$value;
                else
                        throw new TInvalidDataTypeException('datalist_template_required','EditItemTemplate');
        }

        /**
         * @return TTableItemStyle the style for edit item
         */
        public function getEditItemStyle()
        {
                if(($style=$this->getViewState('EditItemStyle',null))===null)
                {
                        $style=new TTableItemStyle;
                        $this->setViewState('EditItemStyle',$style,null);
                }
                return $style;
        }

        /**
         * @return ITemplate the header template
         */
        public function getHeaderTemplate()
        {
                return $this->_headerTemplate;
        }

        /**
         * @param ITemplate the header template
         * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null.
         */
        public function setHeaderTemplate($value)
        {
                if($value instanceof ITemplate || $value===null)
                        $this->_headerTemplate=$value;
                else
                        throw new TInvalidDataTypeException('datalist_template_required','HeaderTemplate');
        }

        /**
         * @return TTableItemStyle the style for header
         */
        public function getHeaderStyle()
        {
                if(($style=$this->getViewState('HeaderStyle',null))===null)
                {
                        $style=new TTableItemStyle;
                        $this->setViewState('HeaderStyle',$style,null);
                }
                return $style;
        }

        /**
         * @return TControl the header item
         */
        public function getHeader()
        {
                return $this->_header;
        }

        /**
         * @return ITemplate the footer template
         */
        public function getFooterTemplate()
        {
                return $this->_footerTemplate;
        }

        /**
         * @param ITemplate the footer template
         * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null.
         */
        public function setFooterTemplate($value)
        {
                if($value instanceof ITemplate || $value===null)
                        $this->_footerTemplate=$value;
                else
                        throw new TInvalidDataTypeException('datalist_template_required','FooterTemplate');
        }

        /**
         * @return TTableItemStyle the style for footer
         */
        public function getFooterStyle()
        {
                if(($style=$this->getViewState('FooterStyle',null))===null)
                {
                        $style=new TTableItemStyle;
                        $this->setViewState('FooterStyle',$style,null);
                }
                return $style;
        }

        /**
         * @return TControl the footer item
         */
        public function getFooter()
        {
                return $this->_footer;
        }

        /**
         * @return ITemplate the template applied when no data is bound to the datalist
         */
        public function getEmptyTemplate()
        {
                return $this->_emptyTemplate;
        }

        /**
         * @param ITemplate the template applied when no data is bound to the datalist
         * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null.
         */
        public function setEmptyTemplate($value)
        {
                if($value instanceof ITemplate || $value===null)
                        $this->_emptyTemplate=$value;
                else
                        throw new TInvalidDataTypeException('datalist_template_required','EmptyTemplate');
        }

        /**
         * @return ITemplate the separator template
         */
        public function getSeparatorTemplate()
        {
                return $this->_separatorTemplate;
        }

        /**
         * @param ITemplate the separator template
         * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null.
         */
        public function setSeparatorTemplate($value)
        {
                if($value instanceof ITemplate || $value===null)
                        $this->_separatorTemplate=$value;
                else
                        throw new TInvalidDataTypeException('datalist_template_required','SeparatorTemplate');
        }

        /**
         * @return TTableItemStyle the style for separator
         */
        public function getSeparatorStyle()
        {
                if(($style=$this->getViewState('SeparatorStyle',null))===null)
                {
                        $style=new TTableItemStyle;
                        $this->setViewState('SeparatorStyle',$style,null);
                }
                return $style;
        }

        /**
         * @return integer the zero-based index of the selected item in {@link getItems Items}.
         * A value -1 means no item selected.
         */
        public function getSelectedItemIndex()
        {
                return $this->getViewState('SelectedItemIndex',-1);
        }

        /**
         * Selects an item by its index in {@link getItems Items}.
         * Previously selected item will be un-selected.
         * If the item to be selected is already in edit mode, it will remain in edit mode.
         * If the index is less than 0, any existing selection will be cleared up.
         * @param integer the selected item index
         */
        public function setSelectedItemIndex($value)
        {
                if(($value=TPropertyValue::ensureInteger($value))<0)
                        $value=-1;
                if(($current=$this->getSelectedItemIndex())!==$value)
                {
                        $this->setViewState('SelectedItemIndex',$value,-1);
                        $items=$this->getItems();
                        $itemCount=$items->getCount();
                        if($current>=0 && $current<$itemCount)
                        {
                                $item=$items->itemAt($current);
                                if(($item instanceof IItemDataRenderer) && $item->getItemType()!==TListItemType::EditItem)
                                        $item->setItemType($current%2?TListItemType::AlternatingItem : TListItemType::Item);
                        }
                        if($value>=0 && $value<$itemCount)
                        {
                                $item=$items->itemAt($value);
                                if(($item instanceof IItemDataRenderer) && $item->getItemType()!==TListItemType::EditItem)
                                        $item->setItemType(TListItemType::SelectedItem);
                        }
                }
        }

        /**
         * @return TControl the selected item, null if no item is selected.
         */
        public function getSelectedItem()
        {
                $index=$this->getSelectedItemIndex();
                $items=$this->getItems();
                if($index>=0 && $index<$items->getCount())
                        return $items->itemAt($index);
                else
                        return null;
        }

        /**
         * @return mixed the key value of the currently selected item
         * @throws TInvalidOperationException if {@link getDataKeyField DataKeyField} is empty.
         */
        public function getSelectedDataKey()
        {
                if($this->getDataKeyField()==='')
                        throw new TInvalidOperationException('datalist_datakeyfield_required');
                $index=$this->getSelectedItemIndex();
                $dataKeys=$this->getDataKeys();
                if($index>=0 && $index<$dataKeys->getCount())
                        return $dataKeys->itemAt($index);
                else
                        return null;
        }

        /**
         * @return integer the zero-based index of the edit item in {@link getItems Items}.
         * A value -1 means no item is in edit mode.
         */
        public function getEditItemIndex()
        {
                return $this->getViewState('EditItemIndex',-1);
        }

        /**
         * Edits an item by its index in {@link getItems Items}.
         * Previously editting item will change to normal item state.
         * If the index is less than 0, any existing edit item will be cleared up.
         * @param integer the edit item index
         */
        public function setEditItemIndex($value)
        {
                if(($value=TPropertyValue::ensureInteger($value))<0)
                        $value=-1;
                if(($current=$this->getEditItemIndex())!==$value)
                {
                        $this->setViewState('EditItemIndex',$value,-1);
                        $items=$this->getItems();
                        $itemCount=$items->getCount();
                        if($current>=0 && $current<$itemCount)
                                $items->itemAt($current)->setItemType($current%2?TListItemType::AlternatingItem : TListItemType::Item);
                        if($value>=0 && $value<$itemCount)
                                $items->itemAt($value)->setItemType(TListItemType::EditItem);
                }
        }

        /**
         * @return TControl the edit item
         */
        public function getEditItem()
        {
                $index=$this->getEditItemIndex();
                $items=$this->getItems();
                if($index>=0 && $index<$items->getCount())
                        return $items->itemAt($index);
                else
                        return null;
        }

        /**
         * @return boolean whether the header should be shown. Defaults to true.
         */
        public function getShowHeader()
        {
                return $this->getViewState('ShowHeader',true);
        }

        /**
         * @param boolean whether to show header
         */
        public function setShowHeader($value)
        {
                $this->setViewState('ShowHeader',TPropertyValue::ensureBoolean($value),true);
        }

        /**
         * @return boolean whether the footer should be shown. Defaults to true.
         */
        public function getShowFooter()
        {
                return $this->getViewState('ShowFooter',true);
        }

        /**
         * @param boolean whether to show footer
         */
        public function setShowFooter($value)
        {
                $this->setViewState('ShowFooter',TPropertyValue::ensureBoolean($value),true);
        }

        /**
         * @return TRepeatInfo repeat information (primarily used by control developers)
         */
        protected function getRepeatInfo()
        {
                if(($repeatInfo=$this->getViewState('RepeatInfo',null))===null)
                {
                        $repeatInfo=new TRepeatInfo;
                        $this->setViewState('RepeatInfo',$repeatInfo,null);
                }
                return $repeatInfo;
        }

        /**
         * @return string caption of the table layout
         */
        public function getCaption()
        {
                return $this->getRepeatInfo()->getCaption();
        }

        /**
         * @param string caption of the table layout
         */
        public function setCaption($value)
        {
                $this->getRepeatInfo()->setCaption($value);
        }

        /**
         * @return TTableCaptionAlign alignment of the caption of the table layout. Defaults to TTableCaptionAlign::NotSet.
         */
        public function getCaptionAlign()
        {
                return $this->getRepeatInfo()->getCaptionAlign();
        }

        /**
         * @return TTableCaptionAlign alignment of the caption of the table layout.
         */
        public function setCaptionAlign($value)
        {
                $this->getRepeatInfo()->setCaptionAlign($value);
        }

        /**
         * @return integer the number of columns that the list should be displayed with. Defaults to 0 meaning not set.
         */
        public function getRepeatColumns()
        {
                return $this->getRepeatInfo()->getRepeatColumns();
        }

        /**
         * @param integer the number of columns that the list should be displayed with.
         */
        public function setRepeatColumns($value)
        {
                $this->getRepeatInfo()->setRepeatColumns($value);
        }

        /**
         * @return TRepeatDirection the direction of traversing the list, defaults to TRepeatDirection::Vertical
         */
        public function getRepeatDirection()
        {
                return $this->getRepeatInfo()->getRepeatDirection();
        }

        /**
         * @param TRepeatDirection the direction of traversing the list
         */
        public function setRepeatDirection($value)
        {
                $this->getRepeatInfo()->setRepeatDirection($value);
        }

        /**
         * @return TRepeatLayout how the list should be displayed, using table or using line breaks. Defaults to TRepeatLayout::Table.
         */
        public function getRepeatLayout()
        {
                return $this->getRepeatInfo()->getRepeatLayout();
        }

        /**
         * @param TRepeatLayout how the list should be displayed, using table or using line breaks
         */
        public function setRepeatLayout($value)
        {
                $this->getRepeatInfo()->setRepeatLayout($value);
        }

        /**
         * This method overrides parent's implementation to handle
         * {@link onItemCommand OnItemCommand} event which is bubbled from
         * datalist items and their child controls.
         * If the event parameter is {@link TDataListCommandEventParameter} and
         * the command name is a recognized one, which includes 'select', 'edit',
         * 'delete', 'update', and 'cancel' (case-insensitive), then a
         * corresponding command event is also raised (such as {@link onEditCommand OnEditCommand}).
         * This method should only be used by control developers.
         * @param TControl the sender of the event
         * @param TEventParameter event parameter
         * @return boolean whether the event bubbling should stop here.
         */
        public function bubbleEvent($sender,$param)
        {
                if($param instanceof TDataListCommandEventParameter)
                {
                        $this->onItemCommand($param);
                        $command=$param->getCommandName();
                        if(strcasecmp($command,self::CMD_SELECT)===0)
                        {
                                if(($item=$param->getItem()) instanceof IItemDataRenderer)
                                        $this->setSelectedItemIndex($item->getItemIndex());
                                $this->onSelectedIndexChanged($param);
                                return true;
                        }
                        else if(strcasecmp($command,self::CMD_EDIT)===0)
                        {
                                $this->onEditCommand($param);
                                return true;
                        }
                        else if(strcasecmp($command,self::CMD_DELETE)===0)
                        {
                                $this->onDeleteCommand($param);
                                return true;
                        }
                        else if(strcasecmp($command,self::CMD_UPDATE)===0)
                        {
                                $this->onUpdateCommand($param);
                                return true;
                        }
                        else if(strcasecmp($command,self::CMD_CANCEL)===0)
                        {
                                $this->onCancelCommand($param);
                                return true;
                        }
                }
                return false;
        }


        /**
         * Raises <b>OnItemCreated</b> event.
         * This method is invoked after a data list item is created and instantiated with
         * template, but before added to the page hierarchy.
         * The datalist item control responsible for the event
         * can be determined from the event parameter.
         * If you override this method, be sure to call parent's implementation
         * so that event handlers have chance to respond to the event.
         * @param TDataListItemEventParameter event parameter
         */
        public function onItemCreated($param)
        {
                $this->raiseEvent('OnItemCreated',$this,$param);
        }

        /**
         * Raises <b>OnItemDataBound</b> event.
         * This method is invoked right after an item is data bound.
         * The datalist item control responsible for the event
         * can be determined from the event parameter.
         * If you override this method, be sure to call parent's implementation
         * so that event handlers have chance to respond to the event.
         * @param TDataListItemEventParameter event parameter
         */
        public function onItemDataBound($param)
        {
                $this->raiseEvent('OnItemDataBound',$this,$param);
        }

        /**
         * Raises <b>OnItemCommand</b> event.
         * This method is invoked when a child control of the data list
         * raises an <b>OnCommand</b> event.
         * @param TDataListCommandEventParameter event parameter
         */
        public function onItemCommand($param)
        {
                $this->raiseEvent('OnItemCommand',$this,$param);
        }

        /**
         * Raises <b>OnEditCommand</b> event.
         * This method is invoked when a child control of the data list
         * raises an <b>OnCommand</b> event and the command name is 'edit' (case-insensitive).
         * @param TDataListCommandEventParameter event parameter
         */
        public function onEditCommand($param)
        {
                $this->raiseEvent('OnEditCommand',$this,$param);
        }

        /**
         * Raises <b>OnDeleteCommand</b> event.
         * This method is invoked when a child control of the data list
         * raises an <b>OnCommand</b> event and the command name is 'delete' (case-insensitive).
         * @param TDataListCommandEventParameter event parameter
         */
        public function onDeleteCommand($param)
        {
                $this->raiseEvent('OnDeleteCommand',$this,$param);
        }

        /**
         * Raises <b>OnUpdateCommand</b> event.
         * This method is invoked when a child control of the data list
         * raises an <b>OnCommand</b> event and the command name is 'update' (case-insensitive).
         * @param TDataListCommandEventParameter event parameter
         */
        public function onUpdateCommand($param)
        {
                $this->raiseEvent('OnUpdateCommand',$this,$param);
        }

        /**
         * Raises <b>OnCancelCommand</b> event.
         * This method is invoked when a child control of the data list
         * raises an <b>OnCommand</b> event and the command name is 'cancel' (case-insensitive).
         * @param TDataListCommandEventParameter event parameter
         */
        public function onCancelCommand($param)
        {
                $this->raiseEvent('OnCancelCommand',$this,$param);
        }

        /**
         * Returns a value indicating whether this control contains header item.
         * This method is required by {@link IRepeatInfoUser} interface.
         * @return boolean whether the datalist has header
         */
        public function getHasHeader()
        {
                return ($this->getShowHeader() && ($this->_headerTemplate!==null || $this->getHeaderRenderer()!==''));
        }

        /**
         * Returns a value indicating whether this control contains footer item.
         * This method is required by {@link IRepeatInfoUser} interface.
         * @return boolean whether the datalist has footer
         */
        public function getHasFooter()
        {
                return ($this->getShowFooter() && ($this->_footerTemplate!==null || $this->getFooterRenderer()!==''));
        }

        /**
         * Returns a value indicating whether this control contains separator items.
         * This method is required by {@link IRepeatInfoUser} interface.
         * @return boolean always false.
         */
        public function getHasSeparators()
        {
                return $this->_separatorTemplate!==null || $this->getSeparatorRenderer()!=='';
        }

        /**
         * Returns a style used for rendering items.
         * This method is required by {@link IRepeatInfoUser} interface.
         * @param string item type (Header,Footer,Item,AlternatingItem,SelectedItem,EditItem,Separator,Pager)
         * @param integer index of the item being rendered
         * @return TStyle item style
         */
        public function generateItemStyle($itemType,$index)
        {
                if(($item=$this->getItem($itemType,$index))!==null && ($item instanceof IStyleable) && $item->getHasStyle())
                {
                        $style=$item->getStyle();
                        $item->clearStyle();
                        return $style;
                }
                else
                        return null;
        }

        /**
         * Renders an item in the list.
         * This method is required by {@link IRepeatInfoUser} interface.
         * @param THtmlWriter writer for rendering purpose
         * @param TRepeatInfo repeat information
         * @param string item type (Header,Footer,Item,AlternatingItem,SelectedItem,EditItem,Separator,Pager)
         * @param integer zero-based index of the item in the item list
         */
        public function renderItem($writer,$repeatInfo,$itemType,$index)
        {
                $item=$this->getItem($itemType,$index);
                if($repeatInfo->getRepeatLayout()===TRepeatLayout::Raw && get_class($item)==='TDataListItem')
                        $item->setTagName('div');
                $item->renderControl($writer);
        }

        /**
         * @param TListItemType item type
         * @param integer item index
         * @return TControl data list item with the specified item type and index
         */
        private function getItem($itemType,$index)
        {
                switch($itemType)
                {
                        case TListItemType::Item:
                        case TListItemType::AlternatingItem:
                        case TListItemType::SelectedItem:
                        case TListItemType::EditItem:
                                return $this->getItems()->itemAt($index);
                        case TListItemType::Header:
                                return $this->getControls()->itemAt(0);
                        case TListItemType::Footer:
                                return $this->getControls()->itemAt($this->getControls()->getCount()-1);
                        case TListItemType::Separator:
                                $i=$index+$index+1;
                                if($this->_headerTemplate!==null || $this->getHeaderRenderer()!=='')
                                        $i++;
                                return $this->getControls()->itemAt($i);
                }
                return null;
        }

        /**
         * Creates a datalist item.
         * This method invokes {@link createItem} to create a new datalist item.
         * @param integer zero-based item index.
         * @param TListItemType item type
         * @return TControl the created item, null if item is not created
         */
        private function createItemInternal($itemIndex,$itemType)
        {
                if(($item=$this->createItem($itemIndex,$itemType))!==null)
                {
                        $param=new TDataListItemEventParameter($item);
                        $this->onItemCreated($param);
                        $this->getControls()->add($item);
                        return $item;
                }
                else
                        return null;
        }

        /**
         * Creates a datalist item and performs databinding.
         * This method invokes {@link createItem} to create a new datalist item.
         * @param integer zero-based item index.
         * @param TListItemType item type
         * @param mixed data to be associated with the item
         * @return TControl the created item, null if item is not created
         */
        private function createItemWithDataInternal($itemIndex,$itemType,$dataItem)
        {
                if(($item=$this->createItem($itemIndex,$itemType))!==null)
                {
                        $param=new TDataListItemEventParameter($item);
                        if($item instanceof IDataRenderer)
                                $item->setData($dataItem);
                        $this->onItemCreated($param);
                        $this->getControls()->add($item);
                        $item->dataBind();
                        $this->onItemDataBound($param);
                        return $item;
                }
                else
                        return null;
        }

        private function getAlternatingItemDisplay()
        {
                if(($classPath=$this->getAlternatingItemRenderer())==='' && $this->_alternatingItemTemplate===null)
                        return array($this->getItemRenderer(),$this->_itemTemplate);
                else
                        return array($classPath,$this->_alternatingItemTemplate);
        }

        private function getSelectedItemDisplay($itemIndex)
        {
                if(($classPath=$this->getSelectedItemRenderer())==='' && $this->_selectedItemTemplate===null)
                {
                        if($itemIndex%2===0)
                                return array($this->getItemRenderer(),$this->_itemTemplate);
                        else
                                return $this->getAlternatingItemDisplay();
                }
                else
                        return array($classPath,$this->_selectedItemTemplate);
        }

        private function getEditItemDisplay($itemIndex)
        {
                if(($classPath=$this->getEditItemRenderer())==='' && $this->_editItemTemplate===null)
                        return $this->getSelectedItemDisplay($itemIndex);
                else
                        return array($classPath,$this->_editItemTemplate);
        }

        /**
         * Creates a datalist item instance based on the item type and index.
         * @param integer zero-based item index
         * @param TListItemType item type
         * @return TControl created datalist item
         */
        protected function createItem($itemIndex,$itemType)
        {
                $template=null;
                $classPath=null;
                switch($itemType)
                {
                        case TListItemType::Item :
                                $classPath=$this->getItemRenderer();
                                $template=$this->_itemTemplate;
                                break;
                        case TListItemType::AlternatingItem :
                                list($classPath,$template)=$this->getAlternatingItemDisplay();
                                break;
                        case TListItemType::SelectedItem:
                                list($classPath,$template)=$this->getSelectedItemDisplay($itemIndex);
                                break;
                        case TListItemType::EditItem:
                                list($classPath,$template)=$this->getEditItemDisplay($itemIndex);
                                break;
                        case TListItemType::Header :
                                $classPath=$this->getHeaderRenderer();
                                $template=$this->_headerTemplate;
                                break;
                        case TListItemType::Footer :
                                $classPath=$this->getFooterRenderer();
                                $template=$this->_footerTemplate;
                                break;
                        case TListItemType::Separator :
                                $classPath=$this->getSeparatorRenderer();
                                $template=$this->_separatorTemplate;
                                break;
                        default:
                                throw new TInvalidDataValueException('datalist_itemtype_unknown',$itemType);
                }
                if($classPath!=='')
                {
                        $item=Prado::createComponent($classPath);
                        if($item instanceof IItemDataRenderer)
                        {
                                $item->setItemIndex($itemIndex);
                                $item->setItemType($itemType);
                        }
                }
                else if($template!==null)
                {
                        $item=new TDataListItem;
                        $item->setItemIndex($itemIndex);
                        $item->setItemType($itemType);
                        $template->instantiateIn($item);
                }
                else
                        $item=null;

                return $item;
        }

        /**
         * Creates empty datalist content.
         */
        protected function createEmptyContent()
        {
                if(($classPath=$this->getEmptyRenderer())!=='')
                        $this->getControls()->add(Prado::createComponent($classPath));
                else if($this->_emptyTemplate!==null)
                        $this->_emptyTemplate->instantiateIn($this);
        }

        /**
         * Applies styles to items, header, footer and separators.
         * Item styles are applied in a hierarchical way. Style in higher hierarchy
         * will inherit from styles in lower hierarchy.
         * Starting from the lowest hierarchy, the item styles include
         * item's own style, {@link getItemStyle ItemStyle}, {@link getAlternatingItemStyle AlternatingItemStyle},
         * {@link getSelectedItemStyle SelectedItemStyle}, and {@link getEditItemStyle EditItemStyle}.
         * Therefore, if background color is set as red in {@link getItemStyle ItemStyle},
         * {@link getEditItemStyle EditItemStyle} will also have red background color
         * unless it is set to a different value explicitly.
         */
        protected function applyItemStyles()
        {
                $itemStyle=$this->getViewState('ItemStyle',null);

                $alternatingItemStyle=$this->getViewState('AlternatingItemStyle',null);
                if($itemStyle!==null)
                {
                        if($alternatingItemStyle===null)
                                $alternatingItemStyle=$itemStyle;
                        else
                                $alternatingItemStyle->mergeWith($itemStyle);
                }

                $selectedItemStyle=$this->getViewState('SelectedItemStyle',null);

                $editItemStyle=$this->getViewState('EditItemStyle',null);
                if($selectedItemStyle!==null)
                {
                        if($editItemStyle===null)
                                $editItemStyle=$selectedItemStyle;
                        else
                                $editItemStyle->mergeWith($selectedItemStyle);
                }

                // apply header style if any
                if($this->_header!==null && $this->_header instanceof IStyleable)
                {
                        if($headerStyle=$this->getViewState('HeaderStyle',null))
                                $this->_header->getStyle()->mergeWith($headerStyle);
                }

                // apply footer style if any
                if($this->_footer!==null && $this->_footer instanceof IStyleable)
                {
                        if($footerStyle=$this->getViewState('FooterStyle',null))
                                $this->_footer->getStyle()->mergeWith($headerStyle);
                }

                $selectedIndex=$this->getSelectedItemIndex();
                $editIndex=$this->getEditItemIndex();

                // apply item styles if any
                foreach($this->getItems() as $index=>$item)
                {
                        if($index===$editIndex)
                                $style=$editItemStyle;
                        else if($index===$selectedIndex)
                                $style=$selectedItemStyle;
                        else if($index%2===0)
                                $style=$itemStyle;
                        else
                                $style=$alternatingItemStyle;
                        if($style && $item instanceof IStyleable)
                                $item->getStyle()->mergeWith($style);
                }

                // apply separator style if any
                if(($separatorStyle=$this->getViewState('SeparatorStyle',null))!==null && $this->getHasSeparators())
                {
                        $controls=$this->getControls();
                        $count=$controls->getCount();
                        for($i=$this->_header?2:1;$i<$count;$i+=2)
                        {
                                if(($separator=$controls->itemAt($i)) instanceof IStyleable)
                                        $separator->getStyle()->mergeWith($separatorStyle);
                        }
                }
        }

        /**
         * Saves item count in viewstate.
         * This method is invoked right before control state is to be saved.
         */
        public function saveState()
        {
                parent::saveState();
                if($this->_items)
                        $this->setViewState('ItemCount',$this->_items->getCount(),0);
                else
                        $this->clearViewState('ItemCount');
        }

        /**
         * Loads item count information from viewstate.
         * This method is invoked right after control state is loaded.
         */
        public function loadState()
        {
                parent::loadState();
                if(!$this->getIsDataBound())
                        $this->restoreItemsFromViewState();
                $this->clearViewState('ItemCount');
        }

        /**
         * Clears up all items in the data list.
         */
        public function reset()
        {
                $this->getControls()->clear();
                $this->getItems()->clear();
                $this->_header=null;
                $this->_footer=null;
        }

        /**
         * Creates data list items based on viewstate information.
         */
        protected function restoreItemsFromViewState()
        {
                $this->reset();
                if(($itemCount=$this->getViewState('ItemCount',0))>0)
                {
                        $items=$this->getItems();
                        $selectedIndex=$this->getSelectedItemIndex();
                        $editIndex=$this->getEditItemIndex();
                        $hasSeparator=$this->_separatorTemplate!==null || $this->getSeparatorRenderer()!=='';
                        $this->_header=$this->createItemInternal(-1,TListItemType::Header);
                        for($i=0;$i<$itemCount;++$i)
                        {
                                if($hasSeparator && $i>0)
                                        $this->createItemInternal($i-1,TListItemType::Separator);
                                if($i===$editIndex)
                                        $itemType=TListItemType::EditItem;
                                else if($i===$selectedIndex)
                                        $itemType=TListItemType::SelectedItem;
                                else
                                        $itemType=$i%2?TListItemType::AlternatingItem : TListItemType::Item;
                                $items->add($this->createItemInternal($i,$itemType));
                        }
                        $this->_footer=$this->createItemInternal(-1,TListItemType::Footer);
                }
                else
                        $this->createEmptyContent();
                $this->clearChildState();
        }

        /**
         * Performs databinding to populate data list items from data source.
         * This method is invoked by dataBind().
         * You may override this function to provide your own way of data population.
         * @param Traversable the data
         */
        protected function performDataBinding($data)
        {
                $this->reset();
                $keys=$this->getDataKeys();
                $keys->clear();
                $keyField=$this->getDataKeyField();
                $itemIndex=0;
                $items=$this->getItems();
                $hasSeparator=$this->_separatorTemplate!==null || $this->getSeparatorRenderer()!=='';
                $selectedIndex=$this->getSelectedItemIndex();
                $editIndex=$this->getEditItemIndex();
                foreach($data as $key=>$dataItem)
                {
                        if($keyField!=='')
                                $keys->add($this->getDataFieldValue($dataItem,$keyField));
                        else
                                $keys->add($key);
                        if($itemIndex===0)
                                $this->_header=$this->createItemWithDataInternal(-1,TListItemType::Header,null);
                        if($hasSeparator && $itemIndex>0)
                                $this->createItemWithDataInternal($itemIndex-1,TListItemType::Separator,null);
                        if($itemIndex===$editIndex)
                                $itemType=TListItemType::EditItem;
                        else if($itemIndex===$selectedIndex)
                                $itemType=TListItemType::SelectedItem;
                        else
                                $itemType=$itemIndex%2?TListItemType::AlternatingItem : TListItemType::Item;
                        $items->add($this->createItemWithDataInternal($itemIndex,$itemType,$dataItem));
                        $itemIndex++;
                }
                if($itemIndex>0)
                        $this->_footer=$this->createItemWithDataInternal(-1,TListItemType::Footer,null);
                else
                {
                        $this->createEmptyContent();
                        $this->dataBindChildren();
                }
                $this->setViewState('ItemCount',$itemIndex,0);
        }

        /**
         * Renders the data list control.
         * This method overrides the parent implementation.
         * @param THtmlWriter writer for rendering purpose.
         */
        public function render($writer)
        {
                if($this->getHasControls())
                {
                        if($this->getItemCount()>0)
                        {
                                $this->applyItemStyles();
                                $repeatInfo=$this->getRepeatInfo();
                                $repeatInfo->renderRepeater($writer,$this);
                        }
                        else if($this->_emptyTemplate!==null || $this->getEmptyRenderer()!=='')
                                parent::render($writer);
                }
        }
}


/**
 * TDataListItemEventParameter class
 *
 * TDataListItemEventParameter encapsulates the parameter data for
 * {@link TDataList::onItemCreated ItemCreated} event of {@link TDataList} controls.
 * The {@link getItem Item} property indicates the DataList item related with the event.
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @version $Id: TDataList.php 2541 2008-10-21 15:05:13Z qiang.xue $
 * @package System.Web.UI.WebControls
 * @since 3.0
 */
class TDataListItemEventParameter extends TEventParameter
{
        /**
         * The datalist item control responsible for the event.
         * @var TControl
         */
        private $_item=null;

        /**
         * Constructor.
         * @param TControl DataList item related with the corresponding event
         */
        public function __construct($item)
        {
                $this->_item=$item;
        }

        /**
         * @return TControl datalist item related with the corresponding event
         */
        public function getItem()
        {
                return $this->_item;
        }
}

/**
 * TDataListCommandEventParameter class
 *
 * TDataListCommandEventParameter encapsulates the parameter data for
 * {@link TDataList::onItemCommand ItemCommand} event of {@link TDataList} controls.
 *
 * The {@link getItem Item} property indicates the DataList item related with the event.
 * The {@link getCommandSource CommandSource} refers to the control that originally
 * raises the Command event.
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @version $Id: TDataList.php 2541 2008-10-21 15:05:13Z qiang.xue $
 * @package System.Web.UI.WebControls
 * @since 3.0
 */
class TDataListCommandEventParameter extends TCommandEventParameter
{
        /**
         * @var TControl the datalist item control responsible for the event.
         */
        private $_item=null;
        /**
         * @var TControl the control originally raises the <b>OnCommand</b> event.
         */
        private $_source=null;

        /**
         * Constructor.
         * @param TControl datalist item responsible for the event
         * @param TControl original event sender
         * @param TCommandEventParameter original event parameter
         */
        public function __construct($item,$source,TCommandEventParameter $param)
        {
                $this->_item=$item;
                $this->_source=$source;
                parent::__construct($param->getCommandName(),$param->getCommandParameter());
        }

        /**
         * @return TControl the datalist item control responsible for the event.
         */
        public function getItem()
        {
                return $this->_item;
        }

        /**
         * @return TControl the control originally raises the <b>OnCommand</b> event.
         */
        public function getCommandSource()
        {
                return $this->_source;
        }
}

/**
 * TDataListItem class
 *
 * A TDataListItem control represents an item in the {@link TDataList} control,
 * such as heading section, footer section, or a data item.
 * The index and data value of the item can be accessed via {@link getItemIndex ItemIndex}>
 * and {@link getDataItem DataItem} properties, respectively. The type of the item
 * is given by {@link getItemType ItemType} property.
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @version $Id: TDataList.php 2541 2008-10-21 15:05:13Z qiang.xue $
 * @package System.Web.UI.WebControls
 * @since 3.0
 */
class TDataListItem extends TWebControl implements INamingContainer, IItemDataRenderer
{
        /**
         * index of the data item in the Items collection of DataList
         */
        private $_itemIndex;
        /**
         * type of the TDataListItem
         * @var TListItemType
         */
        private $_itemType;
        /**
         * value of the data associated with this item
         * @var mixed
         */
        private $_data;

        private $_tagName='span';

        /**
         * Returns the tag name used for this control.
         * @return string tag name of the control to be rendered
         */
        protected function getTagName()
        {
                return $this->_tagName;
        }

        /**
         * @param string tag name of the control to be rendered
         */
        public function setTagName($value)
        {
                $this->_tagName=$value;
        }

        /**
         * Creates a style object for the control.
         * This method creates a {@link TTableItemStyle} to be used by a datalist item.
         * @return TStyle control style to be used
         */
        protected function createStyle()
        {
                return new TTableItemStyle;
        }

        /**
         * @return TListItemType item type
         */
        public function getItemType()
        {
                return $this->_itemType;
        }

        /**
         * @param TListItemType item type.
         */
        public function setItemType($value)
        {
                $this->_itemType=TPropertyValue::ensureEnum($value,'TListItemType');
        }

        /**
         * @return integer zero-based index of the item in the item collection of datalist
         */
        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 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->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)
        {
                return $this->setData($value);
        }

        /**
         * This method overrides parent's implementation by wrapping event parameter
         * for <b>OnCommand</b> event with item information.
         * @param TControl the sender of the event
         * @param TEventParameter event parameter
         * @return boolean whether the event bubbling should stop here.
         */
        public function bubbleEvent($sender,$param)
        {
                if($param instanceof TCommandEventParameter)
                {
                        $this->raiseBubbleEvent($this,new TDataListCommandEventParameter($this,$sender,$param));
                        return true;
                }
                else
                        return false;
        }
}

/**
 * TDataListItemCollection class.
 *
 * TDataListItemCollection represents a collection of data list items.
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @version $Id: TDataList.php 2541 2008-10-21 15:05:13Z qiang.xue $
 * @package System.Web.UI.WebControls
 * @since 3.0
 */
class TDataListItemCollection extends TList
{
        /**
         * Inserts an item at the specified position.
         * This overrides the parent implementation by inserting only TControl descendants.
         * @param integer the speicified position.
         * @param mixed new item
         * @throws TInvalidDataTypeException if the item to be inserted is not a TControl descendant.
         */
        public function insertAt($index,$item)
        {
                if($item instanceof TControl)
                        parent::insertAt($index,$item);
                else
                        throw new TInvalidDataTypeException('datalistitemcollection_datalistitem_required');
        }
}