* @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2014 PradoSoft * @license http://www.pradosoft.com/license/ * @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 Data * 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 ItemIndex property will be set * as the zero-based index of the item in the datalist item collection, and * the ItemType 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 OnCommand event. Therefore, * you can handle all sorts of OnCommand event in a central place by * writing an event handler for {@link onItemCommand OnItemCommand}. * * An additional event is raised if the OnCommand event has one of the following * command names: * - edit: user wants to edit an item. OnEditCommand event will be raised. * - update: user wants to save the change to an item. OnUpdateCommand event will be raised. * - select: user selects an item. OnSelectedIndexChanged event will be raised. * - delete: user deletes an item. OnDeleteCommand event will be raised. * - cancel: user cancels previously editting action. OnCancelCommand 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 * @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 OnItemCreated 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 OnItemDataBound 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 OnItemCommand event. * This method is invoked when a child control of the data list * raises an OnCommand event. * @param TDataListCommandEventParameter event parameter */ public function onItemCommand($param) { $this->raiseEvent('OnItemCommand',$this,$param); } /** * Raises OnEditCommand event. * This method is invoked when a child control of the data list * raises an OnCommand event and the command name is 'edit' (case-insensitive). * @param TDataListCommandEventParameter event parameter */ public function onEditCommand($param) { $this->raiseEvent('OnEditCommand',$this,$param); } /** * Raises OnDeleteCommand event. * This method is invoked when a child control of the data list * raises an OnCommand event and the command name is 'delete' (case-insensitive). * @param TDataListCommandEventParameter event parameter */ public function onDeleteCommand($param) { $this->raiseEvent('OnDeleteCommand',$this,$param); } /** * Raises OnUpdateCommand event. * This method is invoked when a child control of the data list * raises an OnCommand event and the command name is 'update' (case-insensitive). * @param TDataListCommandEventParameter event parameter */ public function onUpdateCommand($param) { $this->raiseEvent('OnUpdateCommand',$this,$param); } /** * Raises OnCancelCommand event. * This method is invoked when a child control of the data list * raises an OnCommand 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($footerStyle); } $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 * @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 * @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 OnCommand 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 OnCommand 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 * @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 OnCommand 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 * @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'); } }