* @link http://www.pradosoft.com/ * @copyright Copyright © 2005 PradoSoft * @license http://www.pradosoft.com/license/ * @version $Revision: $ $Date: $ * @package System.Web.UI.WebControls */ Prado::using('System.Web.UI.WebControls.TBaseDataList'); Prado::using('System.Collections.TPagedDataSource'); Prado::using('System.Collections.TDummyDataSource'); Prado::using('System.Web.UI.WebControls.TTable'); /** * TDataGrid class * * TDataGrid represents a data bound and updatable grid control. * * To use TDataGrid, sets its DataSource property and invokes dataBind() * afterwards. The data will be populated into the TDataGrid and saved in the Items property. * * Each item is associated with a row of data and will be displayed as a row in table. * A data item can be at one of three states: normal, selected and edit. * The state determines how the item will be displayed. For example, if an item * is in edit state, it may be displayed as a table row with input text boxes instead * static text as in normal state. * To change the state of a data item, set either the EditItemIndex property * or the SelectedItemIndex property. * * A datagrid is specified with a list of columns. Each column specifies how the corresponding * table column will be displayed. For example, the header/footer text of that column, * the cells in that column, and so on. The following column types are provided by the framework, * - TBoundColumn, associated with a specific field in datasource and displays the corresponding data. * - TEditCommandColumn, displaying edit/update/cancel command buttons * - TButtonColumn, displaying generic command buttons that may be bound to specific field in datasource. * - THyperLinkColumn, displaying a hyperlink that may be boudn to specific field in datasource. * - TTemplateColumn, displaying content based on templates. * * There are three ways to specify columns for a datagrid. * * Note, automatically generated columns cannot be accessed via Columns property. * * TDataGrid supports sorting. If the AllowSorting is set to true, a column * whose SortExpression is not empty will have its header text displayed as a link button. * Clicking on the link button will raise OnSortCommand event. You can respond to this event, * sort the data source according to the event parameter, and then invoke databind on the datagrid. * * TDataGrid supports paging. If the AllowPaging is set to true, a pager will be displayed * on top and/or bottom of the table. How the pager will be displayed is determined by PagerDisplay * and PagerButtonCount properties. The former decides the position of the pager and latter * specifies how many buttons are to be used for paging purpose. Clicking on a pager button will raise * an onPageIndexChanged event. You can respond to this event, specify the page to be displayed by * setting CurrentPageIndex property, and then invoke databind on the datagrid. * * TDataGrid supports two kinds of paging. The first one is based on the number of data items in * datasource. The number of pages PageCount is calculated based the item number and the * PageSize property. The datagrid will manage which section of the data source to be displayed * based on the CurrentPageIndex property. * The second approach calculates the page number based on the VirtualItemCount property and * the PageSize property. The datagrid will always display from the beginning of the datasource * upto the number of PageSize> data items. This approach is especially useful when the datasource may * contain too many data items to be managed by the datagrid efficiently. * * When the datagrid contains a button control that raises an OnCommand * event, the event will be bubbled up to the datagrid control. * If the event's command name is recognizable by the datagrid control, * a corresponding item event will be raised. The following item events will be * raised upon a specific command: * - OnEditCommand, edit * - OnCancelCommand, cancel * - OnSelectCommand, select * - OnDeleteCommand, delete * - OnUpdateCommand, update * - onPageIndexChanged, page * - OnSortCommand, sort * The data list will always raise an OnItemCommand * upon its receiving a bubbled OnCommand event. * * An OnItemCreated event will be raised right after each item is created in the datagrid. * * @author Qiang Xue * @version $Revision: $ $Date: $ * @package System.Web.UI.WebControls * @since 3.0 */ class TDataGrid extends TBaseDataList implements INamingContainer { private $_columns=null; private $_autoColumns=null; private $_items=null; private $_header=null; private $_footer=null; private $_pagedDataSource=null; /** * @return string tag name of the datagrid */ protected function getTagName() { return 'table'; } public function addParsedObject($object) { if($object instanceof TDataGridColumn) $this->getColumns()->add($object); } public function getColumns() { if(!$this->_columns) $this->_columns=new TDataGridColumnCollection; return $this->_columns; } public function getAutoColumns() { if(!$this->_autoColumns) $this->_autoColumns=new TDataGridColumnCollection; return $this->_autoColumns; } public function getItems() { if(!$this->_items) $this->_items=new TDataGridItemCollection; return $this->_items; } /** * Creates a style object for the control. * This method creates a {@link TTableStyle} to be used by datagrid. * @return TStyle control style to be used */ protected function createStyle() { $style=new TTableStyle; $style->setGridLines('Both'); $style->setCellSpacing(0); return $style; } /** * @return string the URL of the background image for the datagrid */ public function getBackImageUrl() { return $this->getStyle()->getBackImageUrl(); } /** * Sets the URL of the background image for the datagrid * @param string the URL */ public function setBackImageUrl($value) { $this->getStyle()->setBackImageUrl($value); } /** * @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 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 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 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 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 TDataGridItem the header item */ public function getHeader() { return $this->_header; } /** * @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 TDataGridItem the footer item */ public function getFooter() { return $this->_footer; } /** * @return TDataGridPagerStyle the style for pager */ public function getPagerStyle() { if(($style=$this->getViewState('PagerStyle',null))===null) { $style=new TDataGridPagerStyle; $this->setViewState('PagerStyle',$style,null); } return $style; } /** * @return TDataGridItem 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 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->getItemType()!=='EditItem') $item->setItemType($current%2?'AlternatingItem':'Item'); } if($value>=0 && $value<$itemCount) { $item=$items->itemAt($value); if($item->getItemType()!=='EditItem') $item->setItemType('SelectedItem'); } } } /** * @return TDataGridItem 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 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?'AlternatingItem':'Item'); if($value>=0 && $value<$itemCount) $items->itemAt($value)->setItemType('EditItem'); } } /** * @return boolean whether the custom paging is enabled Defaults to false. */ public function getAllowCustomPaging() { return $this->getViewState('AllowCustomPaging',false); } /** * @param boolean whether the custom paging is enabled */ public function setAllowCustomPaging($value) { $this->setViewState('AllowCustomPaging',TPropertyValue::ensureBoolean($value),false); } /** * @return boolean whether paging is enabled. Defaults to false. */ public function getAllowPaging() { return $this->getViewState('AllowPaging',false); } /** * @param boolean whether paging is enabled */ public function setAllowPaging($value) { $this->setViewState('AllowPaging',TPropertyValue::ensureBoolean($value),false); } /** * @return boolean whether sorting is enabled. Defaults to false. */ public function getAllowSorting() { return $this->getViewState('AllowSorting',false); } /** * @param boolean whether sorting is enabled */ public function setAllowSorting($value) { $this->setViewState('AllowSorting',TPropertyValue::ensureBoolean($value),false); } /** * @return boolean whether datagrid columns should be automatically generated. Defaults to true. */ public function getAutoGenerateColumns() { return $this->getViewState('AutoGenerateColumns',true); } /** * @param boolean whether datagrid columns should be automatically generated */ public function setAutoGenerateColumns($value) { $this->setViewState('AutoGenerateColumns',TPropertyValue::ensureBoolean($value),true); } /** * @return integer the index of the current page. Defaults to 0. */ public function getCurrentPageIndex() { return $this->getViewState('CurrentPageIndex',0); } /** * @param integer the index of the current page */ public function setCurrentPageIndex($value) { if(($value=TPropertyValue::ensureInteger($value))<0) throw new TInvalidDataValueException('datagrid_currentpageindex_invalid'); $this->setViewState('CurrentPageIndex',$value,0); } /** * @return integer the number of rows displayed within a page. Defaults to 10. */ public function getPageSize() { return $this->getViewState('PageSize',10); } /** * @param integer the number of rows displayed within a page */ public function setPageSize($value) { if(($value=TPropertyValue::ensureInteger($value))<1) throw new TInvalidDataValueException('datagrid_pagesize_invalid'); $this->setViewState('PageSize',TPropertyValue::ensureInteger($value),10); } public function getPageCount() { if($this->_pagedDataSource) return $this->_pagedDataSource->getPageCount(); return $this->getViewState('PageCount',0); } /** * @return integer virtual number of items in the grid. Defaults to 0, meaning not set. */ public function getVirtualItemCount() { return $this->getViewState('VirtualItemCount',0); } /** * @param integer virtual number of items in the grid */ public function setVirtualItemCount($value) { if(($value=TPropertyValue::ensureInteger($value))<0) throw new TInvalidDataValueException('datagrid_virtualitemcount_invalid'); $this->setViewState('VirtualItemCount',$value,0); } /** * @return boolean whether the header should be displayed Defaults to true. */ public function getShowHeader() { return $this->getViewState('ShowHeader',true); } /** * @param boolean whether the header should be displayed */ public function setShowHeader($value) { $this->setViewState('ShowHeader',TPropertyValue::ensureBoolean($value),true); } /** * @return boolean whether the footer should be displayed. Defaults to false. */ public function getShowFooter() { return $this->getViewState('ShowFooter',false); } /** * @param boolean whether the footer should be displayed */ public function setShowFooter($value) { $this->setViewState('ShowFooter',TPropertyValue::ensureBoolean($value),false); } /** * Handles BubbleEvent. * This method overrides parent's implementation to handle * {@link onItemCommand OnItemCommand} event which is bubbled from * {@link TDataGridItem} child controls. * If the event parameter is {@link TDataGridCommandEventParameter} 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 onBubbleEvent($sender,$param) { if($param instanceof TDataGridCommandEventParameter) { $this->onItemCommand($param); $command=$param->getCommandName(); if(strcasecmp($command,'select')===0) { $this->setSelectedIndex($param->getItem()->getItemIndex()); $this->onSelectedIndexChanged(null); return true; } else if(strcasecmp($command,'edit')===0) { $this->onEditCommand($param); return true; } else if(strcasecmp($command,'delete')===0) { $this->onDeleteCommand($param); return true; } else if(strcasecmp($command,'update')===0) { $this->onUpdateCommand($param); return true; } else if(strcasecmp($command,'cancel')===0) { $this->onCancelCommand($param); return true; } else if(strcasecmp($command,'sort')===0) { $this->onSortCommand(new TDataGridSortCommandEventParameter($sender,$param)); return true; } else if(strcasecmp($command,'page')===0) { $p=$param->getCommandParameter(); if(strcasecmp($p,'next')===0) $pageIndex=$this->getCurrentPageIndex()+1; else if(strcasecmp($p,'prev')===0) $pageIndex=$this->getCurrentPageIndex()-1; else $pageIndex=TPropertyValue::ensureInteger($p)-1; $this->onPageIndexChanged(new TDataGridPageChangedEventParameter($sender,$pageIndex)); return true; } } return false; } /** * Raises OnCancelCommand event. * This method is invoked when a button control raisesCommand event * withcancel command name. * @param TDataGridCommandEventParameter event parameter */ public function onCancelCommand($param) { $this->raiseEvent('OnCancelCommand',$this,$param); } /** * Raises OnDeleteCommand event. * This method is invoked when a button control raises Command event * with delete command name. * @param TDataGridCommandEventParameter event parameter */ public function onDeleteCommand($param) { $this->raiseEvent('OnDeleteCommand',$this,$param); } /** * Raises OnEditCommand event. * This method is invoked when a button control raises Command event * with edit command name. * @param TDataGridCommandEventParameter event parameter */ public function onEditCommand($param) { $this->raiseEvent('OnEditCommand',$this,$param); } /** * Raises OnItemCommand event. * This method is invoked when a button control raises Command event. * @param TDataGridItemCommandEventParameter event parameter */ public function onItemCommand($param) { $this->raiseEvent('OnItemCommand',$this,$param); } /** * Raises OnSortCommand event. * This method is invoked when a button control raises Command event * with sort command name. * @param TDataGridSortCommandEventParameter event parameter */ public function onSortCommand($param) { $this->raiseEvent('OnSortCommand',$this,$param); } /** * Raises OnUpdateCommand event. * This method is invoked when a button control raises Command event * with update command name. * @param TDataGridCommandEventParameter event parameter */ public function onUpdateCommand($param) { $this->raiseEvent('OnUpdateCommand',$this,$param); } /** * Raises OnItemCreated event. * This method is invoked right after a datagrid item is created and before * added to page hierarchy. * @param TDataGridItemEventParameter event parameter */ public function onItemCreated($param) { $this->raiseEvent('OnItemCreated',$this,$param); } /** * Raises OnItemDataBound event. * This method is invoked for each datagrid item after it performs * databinding. * @param TDataGridItemEventParameter event parameter */ public function onItemDataBound($param) { $this->raiseEvent('OnItemDataBound',$this,$param); } /** * Raises OnPageIndexChanged event. * This method is invoked when current page is changed. * @param TDataGridPageChangedEventParameter event parameter */ public function onPageIndexChanged($param) { $this->raiseEvent('OnPageIndexChanged',$this,$param); } /** * Saves item count in viewstate. * This method is invoked right before control state is to be saved. */ public function saveState() { parent::saveState(); if(!$this->getEnableViewState(true)) return; if($this->_items) $this->setViewState('ItemCount',$this->_items->getCount(),0); else $this->clearViewState('ItemCount'); if($this->_autoColumns) { $state=array(); foreach($this->_autoColumns as $column) $state[]=$column->saveState(); $this->setViewState('AutoColumns',$state,array()); } else $this->clearViewState('AutoColumns'); if($this->_columns) { $state=array(); foreach($this->_columns as $column) $state[]=$column->saveState(); $this->setViewState('Columns',$state,array()); } else $this->clearViewState('Columns'); } /** * Loads item count information from viewstate. * This method is invoked right after control state is loaded. */ public function loadState() { parent::loadState(); if(!$this->getEnableViewState(true)) return; if(!$this->getIsDataBound()) { $state=$this->getViewState('AutoColumns',array()); if(!empty($state)) { $this->_autoColumns=new TDataGridColumnCollection; foreach($state as $st) { $column=new TBoundColumn; $column->loadState($st); $this->_autoColumns->add($column); } } else $this->_autoColumns=null; $state=$this->getViewState('Columns',array()); if($this->_columns && $this->_columns->getCount()===count($state)) { $i=0; foreach($this->_columns as $column) { $column->loadState($state[$i]); $i++; } } $this->restoreGridFromViewState(); } $this->clearViewState('ItemCount'); } private function createPagedDataSource() { $ds=new TPagedDataSource; $ds->setCurrentPageIndex($this->getCurrentPageIndex()); $ds->setPageSize($this->getPageSize()); $ds->setAllowPaging($this->getAllowPaging()); $ds->setAllowCustomPaging($this->getAllowCustomPaging()); $ds->setVirtualItemCount($this->getVirtualItemCount()); return $ds; } /** * Clears up all items in the data list. */ public function reset() { $this->getControls()->clear(); $this->getItems()->clear(); $this->_header=null; $this->_footer=null; } protected function restoreGridFromViewState() { $this->reset(); $itemCount=$this->getViewState('ItemCount',0); $this->_pagedDataSource=$ds=$this->createPagedDataSource(); $allowPaging=$ds->getAllowPaging(); if($allowPaging && $ds->getAllowCustomPaging()) $ds->setDataSource(new TDummyDataSource($itemCount)); else $ds->setDataSource(new TDummyDataSource($this->getViewState('DataSourceCount',0))); $columns=new TList($this->getColumns()); $columns->mergeWith($this->_autoColumns); $items=$this->getItems(); $items->clear(); if(($columnCount=$columns->getCount())>0) { foreach($columns as $column) $column->initialize(); if($allowPaging) $this->createPager(-1,-1,$columnCount,$ds); $this->_header=$this->createItemInternal(-1,-1,'Header',false,null,$columns); $selectedIndex=$this->getSelectedItemIndex(); $editIndex=$this->getEditItemIndex(); $index=0; $dsIndex=$ds->getAllowPaging()?$ds->getFirstIndexInPage():0; foreach($ds as $data) { if($index===$editIndex) $itemType='EditItem'; else if($index===$selectedIndex) $itemType='SelectedItem'; else if($index % 2) $itemType='AlternatingItem'; else $itemType='Item'; $items->add($this->createItemInternal($index,$dsIndex,$itemType,false,null,$columns)); $index++; $dsIndex++; } $this->_footer=$this->createItemInternal(-1,-1,'Footer',false,null,$columns); if($allowPaging) $this->createPager(-1,-1,$columnCount,$ds); } $this->_pagedDataSource=null; } /** * 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(); $this->_pagedDataSource=$ds=$this->createPagedDataSource(); $ds->setDataSource($data); $allowPaging=$ds->getAllowPaging(); if($allowPaging && $ds->getCurrentPageIndex()>=$ds->getPageCount()) throw new TInvalidDataValueException('datagrid_currentpageindex_invalid'); // get all columns if($this->getAutoGenerateColumns()) { $columns=new TList($this->getColumns()); $autoColumns=$this->createAutoColumns($ds); $columns->mergeWith($autoColumns); } else $columns=$this->getColumns(); $items=$this->getItems(); if(($columnCount=$columns->getCount())>0) { foreach($columns as $column) $column->initialize(); $allowPaging=$ds->getAllowPaging(); if($allowPaging) $this->createPager(-1,-1,$columnCount,$ds); $this->_header=$this->createItemInternal(-1,-1,'Header',true,null,$columns); $selectedIndex=$this->getSelectedItemIndex(); $editIndex=$this->getEditItemIndex(); $index=0; $dsIndex=$ds->getAllowPaging()?$ds->getFirstIndexInPage():0; foreach($ds as $data) { if($keyField!=='') $keys->add($this->getDataFieldValue($data,$keyField)); if($index===$editIndex) $itemType='EditItem'; else if($index===$selectedIndex) $itemType='SelectedItem'; else if($index % 2) $itemType='AlternatingItem'; else $itemType='Item'; $items->add($this->createItemInternal($index,$dsIndex,$itemType,true,$data,$columns)); $index++; $dsIndex++; } $this->_footer=$this->createItemInternal(-1,-1,'Footer',true,null,$columns); if($allowPaging) $this->createPager(-1,-1,$columnCount,$ds); $this->setViewState('ItemCount',$index,0); $this->setViewState('PageCount',$ds->getPageCount(),0); $this->setViewState('DataSourceCount',$ds->getDataSourceCount(),0); } else { $this->clearViewState('ItemCount'); $this->clearViewState('PageCount'); $this->clearViewState('DataSourceCount'); } $this->_pagedDataSource=null; } /** * Creates a datagrid item instance based on the item type and index. * @param integer zero-based item index * @param string item type, may be 'Header', 'Footer', 'Item', 'Separator', 'AlternatingItem', 'SelectedItem', 'EditItem'. * @return TDataGridItem created data list item */ protected function createItem($itemIndex,$dataSourceIndex,$itemType) { return new TDataGridItem($itemIndex,$dataSourceIndex,$itemType); } private function createItemInternal($itemIndex,$dataSourceIndex,$itemType,$dataBind,$dataItem,$columns) { $item=$this->createItem($itemIndex,$dataSourceIndex,$itemType); $this->initializeItem($item,$columns); $param=new TDataGridItemEventParameter($item); if($dataBind) { $item->setDataItem($dataItem); $this->onItemCreated($param); $this->getControls()->add($item); $item->dataBind(); $this->onItemDataBound($param); $item->setDataItem(null); } else { $this->onItemCreated($param); $this->getControls()->add($item); } return $item; } protected function initializeItem($item,$columns) { $cells=$item->getCells(); $itemType=$item->getItemType(); $index=0; foreach($columns as $column) { if($itemType==='Header') $cell=new TTableHeaderCell; else $cell=new TTableCell; $column->initializeCell($cell,$index,$itemType); $cells->add($cell); $index++; } } private function createPager($itemIndex,$dataSourceIndex,$columnSpan,$pagedDataSource) { $item=$this->createItem($itemIndex,$dataSourceIndex,'Pager'); $this->initializePager($item,$columnSpan,$pagedDataSource); $this->onItemCreated(new TDataGridItemEventParameter($item)); $this->getControls()->add($item); return $item; } protected function initializePager($pager,$columnSpan,$pagedDataSource) { $cell=new TTableCell; if($columnSpan>1) $cell->setColumnSpan($columnSpan); $this->buildPager($cell,$pagedDataSource); $pager->getCells()->add($cell); } protected function buildPager($cell,$dataSource) { switch($this->getPagerStyle()->getMode()) { case 'NextPrev': $this->buildNextPrevPager($cell,$dataSource); break; case 'Numeric': $this->buildNumericPager($cell,$dataSource); break; } } protected function buildNextPrevPager($cell,$dataSource) { $style=$this->getPagerStyle(); $controls=$cell->getControls(); if($dataSource->getIsFirstPage()) { $label=new TLabel; $label->setText($style->getPrevPageText()); $controls->add($label); } else { $button=new TLinkButton; $button->setText($style->getPrevPageText()); $button->setCommandName('page'); $button->setCommandParameter('prev'); $button->setCausesValidation(false); $controls->add($button); } $controls->add(' '); if($dataSource->getIsLastPage()) { $label=new TLabel; $label->setText($style->getNextPageText()); $controls->add($label); } else { $button=new TLinkButton; $button->setText($style->getNextPageText()); $button->setCommandName('page'); $button->setCommandParameter('next'); $button->setCausesValidation(false); $controls->add($button); } } protected function buildNumericPager($cell,$dataSource) { $style=$this->getPagerStyle(); $controls=$cell->getControls(); $pageCount=$dataSource->getPageCount(); $pageIndex=$dataSource->getCurrentPageIndex()+1; $maxButtonCount=$style->getPageButtonCount(); $buttonCount=$maxButtonCount>$pageCount?$pageCount:$maxButtonCount; $startPageIndex=1; $endPageIndex=$buttonCount; if($pageIndex>$endPageIndex) { $startPageIndex=((int)(($pageIndex-1)/$maxButtonCount))*$maxButtonCount+1; if(($endPageIndex=$startPageIndex+$maxButtonCount-1)>$pageCount) $endPageIndex=$pageCount; if($endPageIndex-$startPageIndex+1<$maxButtonCount) { if(($startPageIndex=$endPageIndex-$maxButtonCount+1)<1) $startPageIndex=1; } } if($startPageIndex>1) { $button=new TLinkButton; $button->setText($style->getPrevPageText()); $button->setCommandName('page'); $button->setCommandParameter($startPageIndex-1); $button->setCausesValidation(false); $controls->add($button); $controls->add(' '); } for($i=$startPageIndex;$i<=$endPageIndex;++$i) { if($i===$pageIndex) { $label=new TLabel; $label->setText("$i"); $controls->add($label); } else { $button=new TLinkButton; $button->setText("$i"); $button->setCommandName('page'); $button->setCommandParameter($i); $button->setCausesValidation(false); $controls->add($button); } if($i<$endPageIndex) $controls->add(' '); } if($pageCount>$endPageIndex) { $controls->add(' '); $button=new TLinkButton; $button->setText($style->getNextPageText()); $button->setCommandName('page'); $button->setCommandParameter($endPageIndex+1); $button->setCausesValidation(false); $controls->add($button); } } protected function createAutoColumns($dataSource) { if(!$dataSource) return null; $autoColumns=$this->getAutoColumns(); $autoColumns->clear(); foreach($dataSource as $row) { foreach($row as $key=>$value) { $column=new TBoundColumn; if(is_string($key)) { $column->setHeaderText($key); $column->setDataField($key); $column->setSortExpression($key); $column->setOwner($this); $autoColumns->add($column); } else { $column->setHeaderText('Item'); $column->setDataField($key); $column->setSortExpression('Item'); $column->setOwner($this); $autoColumns->add($column); } } break; } return $autoColumns; } /** * 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=new TTableItemStyle($itemStyle); if(($style=$this->getViewState('AlternatingItemStyle',null))!==null) $alternatingItemStyle->mergeWith($style); $selectedItemStyle=$this->getViewState('SelectedItemStyle',null); $editItemStyle=new TTableItemStyle($selectedItemStyle); if(($style=$this->getViewState('EditItemStyle',null))!==null) $editItemStyle->copyFrom($style); $headerStyle=$this->getViewState('HeaderStyle',null); $footerStyle=$this->getViewState('FooterStyle',null); $pagerStyle=$this->getViewState('PagerStyle',null); $separatorStyle=$this->getViewState('SeparatorStyle',null); $invisibleColumns=0; if($this->_columns) { foreach($this->_columns as $column) if(!$column->getVisible()) $invisibleColumns++; } foreach($this->getControls() as $index=>$item) { $itemType=$item->getItemType(); switch($itemType) { case 'Header': if($headerStyle) $item->getStyle()->mergeWith($headerStyle); if(!$this->getShowHeader()) $item->setVisible(false); break; case 'Footer': if($footerStyle) $item->getStyle()->mergeWith($footerStyle); if(!$this->getShowFooter()) $item->setVisible(false); break; case 'Separator': if($separatorStyle) $item->getStyle()->mergeWith($separatorStyle); break; case 'Item': if($itemStyle) $item->getStyle()->mergeWith($itemStyle); break; case 'AlternatingItem': if($alternatingItemStyle) $item->getStyle()->mergeWith($alternatingItemStyle); break; case 'SelectedItem': if($index % 2==1) { if($itemStyle) $item->getStyle()->mergeWith($itemStyle); } else { if($alternatingItemStyle) $item->getStyle()->mergeWith($alternatingItemStyle); } if($selectedItemStyle) $item->getStyle()->mergeWith($selectedItemStyle); break; case 'EditItem': if($index % 2==1) { if($itemStyle) $item->getStyle()->mergeWith($itemStyle); } else { if($alternatingItemStyle) $item->getStyle()->mergeWith($alternatingItemStyle); } if($editItemStyle) $item->getStyle()->mergeWith($editItemStyle); break; case 'Pager': if($pagerStyle) { $item->getStyle()->mergeWith($pagerStyle); if($index===0) { if($pagerStyle->getPosition()==='Bottom' || !$pagerStyle->getVisible()) $item->setVisible(false); } else { if($pagerStyle->getPosition()==='Top' || !$pagerStyle->getVisible()) $item->setVisible(false); } } break; default: break; } if($this->_columns && $itemType!=='Pager') { $n=$this->_columns->getCount(); $cells=$item->getCells(); for($i=0;$i<$n;++$i) { $cell=$cells->itemAt($i); $column=$this->_columns->itemAt($i); if(!$column->getVisible()) $cell->setVisible(false); else { if($itemType==='Header') $style=$column->getHeaderStyle(false); else if($itemType==='Footer') $style=$column->getFooterStyle(false); else $style=$column->getItemStyle(false); if($style!==null) $cell->getStyle()->mergeWith($style); } } } else if($itemType==='Pager' && $invisibleColumns>0) { $cell=$item->getCells()->itemAt(0); $cell->setColumnSpan($cell->getColumnSpan()-$invisibleColumns); } } } public function renderContents($writer) { if($this->getHasControls()) { $this->applyItemStyles(); parent::renderContents($writer); } } } /** * TDataGridItemEventParameter class * * TDataGridItemEventParameter encapsulates the parameter data for * {@link TDataGrid::onItemCreated ItemCreated} event of {@link TDataGrid} controls. * The {@link getItem Item} property indicates the datagrid item related with the event. * * @author Qiang Xue * @version $Revision: $ $Date: $ * @package System.Web.UI.WebControls * @since 3.0 */ class TDataGridItemEventParameter extends TEventParameter { /** * The TDataGridItem control responsible for the event. * @var TDataGridItem */ private $_item=null; /** * Constructor. * @param TDataGridItem datagrid item related with the corresponding event */ public function __construct(TDataGridItem $item) { $this->_item=$item; } /** * @return TDataGridItem datagrid item related with the corresponding event */ public function getItem() { return $this->_item; } } /** * TDataGridCommandEventParameter class * * TDataGridCommandEventParameter encapsulates the parameter data for * {@link TDataGrid::onItemCommand ItemCommand} event of {@link TDataGrid} controls. * * The {@link getItem Item} property indicates the datagrid item related with the event. * The {@link getCommandSource CommandSource} refers to the control that originally * raises the Command event. * * @author Qiang Xue * @version $Revision: $ $Date: $ * @package System.Web.UI.WebControls * @since 3.0 */ class TDataGridCommandEventParameter extends TCommandEventParameter { /** * @var TDataGridItem the TDataGridItem control responsible for the event. */ private $_item=null; /** * @var TControl the control originally raises the Command event. */ private $_source=null; /** * Constructor. * @param TDataGridItem datagrid 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 TDataGridItem the TDataGridItem control responsible for the event. */ public function getItem() { return $this->_item; } /** * @return TControl the control originally raises the Command event. */ public function getCommandSource() { return $this->_source; } } /** * TDataGridSortCommandEventParameter class * * TDataGridSortCommandEventParameter encapsulates the parameter data for * {@link TDataGrid::onSortCommand SortCommand} event of {@link TDataGrid} controls. * * The {@link getCommandSource CommandSource} property refers to the control * that originally raises the Command event, while {@link getSortExpression SortExpression} * gives the sort expression carried with the sort command. * * @author Qiang Xue * @version $Revision: $ $Date: $ * @package System.Web.UI.WebControls * @since 3.0 */ class TDataGridSortCommandEventParameter extends TEventParameter { /** * @var string sort expression */ private $_sortExpression=''; /** * @var TControl original event sender */ private $_source=null; /** * Constructor. * @param TControl the control originally raises the Command event. * @param TDataGridCommandEventParameter command event parameter */ public function __construct($source,TDataGridCommandEventParameter $param) { $this->_source=$source; $this->_sortExpression=$param->getCommandParameter(); } /** * @return TControl the control originally raises the Command event. */ public function getCommandSource() { return $this->_source; } /** * @return string sort expression */ public function getSortExpression() { return $this->_sortExpression; } } /** * TDataGridPageChangedEventParameter class * * TDataGridPageChangedEventParameter encapsulates the parameter data for * {@link TDataGrid::onPageIndexChanged PageIndexChanged} event of {@link TDataGrid} controls. * * The {@link getCommandSource CommandSource} property refers to the control * that originally raises the Command event, while {@link getNewPageIndex NewPageIndex} * returns the new page index carried with the page command. * * @author Qiang Xue * @version $Revision: $ $Date: $ * @package System.Web.UI.WebControls * @since 3.0 */ class TDataGridPageChangedEventParameter extends TEventParameter { /** * @var integer new page index */ private $_newIndex; /** * @var TControl original event sender */ private $_source=null; /** * Constructor. * @param TControl the control originally raises the Command event. * @param integer new page index */ public function __construct($source,$newPageIndex) { $this->_source=$source; $this->_newIndex=$newPageIndex; } /** * @return TControl the control originally raises the Command event. */ public function getCommandSource() { return $this->_source; } /** * @return integer new page index */ public function getNewPageIndex() { return $this->_newIndex; } } /** * TDataGridItem class * * A TDataGridItem control represents an item in the {@link TDataGrid} 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. Property {@link getDataSourceIndex DataSourceIndex} * gives the index of the item from the bound data source. * * @author Qiang Xue * @version $Revision: $ $Date: $ * @package System.Web.UI.WebControls * @since 3.0 */ class TDataGridItem extends TTableRow implements INamingContainer { /** * @var integer index of the data item in the Items collection of datagrid */ private $_itemIndex=''; /** * @var integer index of the item from the bound data source */ private $_dataSourceIndex=0; /** * type of the TDataGridItem * @var string */ private $_itemType=''; /** * value of the data item * @var mixed */ private $_dataItem=null; /** * Constructor. * @param integer zero-based index of the item in the item collection of datagrid * @param string item type, can be 'Header','Footer','Item','AlternatingItem','SelectedItem','EditItem','Separator','Pager'. */ public function __construct($itemIndex,$dataSourceIndex,$itemType) { $this->_itemIndex=$itemIndex; $this->_dataSourceIndex=$dataSourceIndex; $this->setItemType($itemType); } /** * @return string item type, can be 'Header','Footer','Item','AlternatingItem','SelectedItem','EditItem','Separator','Pager' */ public function getItemType() { return $this->_itemType; } /** * @param mixed data to be associated with the item */ public function setItemType($value) { $this->_itemType=TPropertyValue::ensureEnum($value,'Header','Footer','Item','AlternatingItem','SelectedItem','EditItem','Separator','Pager'); } /** * @return integer zero-based index of the item in the item collection of datagrid */ public function getItemIndex() { return $this->_itemIndex; } /** * @return integer the index of the datagrid item from the bound data source */ public function getDataSourceIndex() { return $this->_dataSourceIndex; } /** * @return mixed data associated with the item */ public function getDataItem() { return $this->_dataItem; } /** * @param mixed data to be associated with the item */ public function setDataItem($value) { $this->_dataItem=$value; } /** * Handles BubbleEvent. * This method overrides parent's implementation by wrapping event parameter * for Command 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 onBubbleEvent($sender,$param) { if($param instanceof TCommandEventParameter) { $this->raiseBubbleEvent($this,new TDataGridCommandEventParameter($this,$sender,$param)); return true; } else return false; } } /** * TDataGridItemCollection class. * * TDataGridItemCollection represents a collection of data grid items. * * @author Qiang Xue * @version $Revision: $ $Date: $ * @package System.Web.UI.WebControls * @since 3.0 */ class TDataGridItemCollection extends TList { /** * Inserts an item at the specified position. * This overrides the parent implementation by inserting only TDataGridItem. * @param integer the speicified position. * @param mixed new item * @throws TInvalidDataTypeException if the item to be inserted is not a TDataGridItem. */ public function insertAt($index,$item) { if($item instanceof TDataGridItem) parent::insertAt($index,$item); else throw new TInvalidDataTypeException('datagriditemcollection_datagriditem_required'); } } /** * TDataGridColumnCollection class. * * TDataGridColumnCollection represents a collection of data grid columns. * * @author Qiang Xue * @version $Revision: $ $Date: $ * @package System.Web.UI.WebControls * @since 3.0 */ class TDataGridColumnCollection extends TList { /** * Inserts an item at the specified position. * This overrides the parent implementation by inserting only TDataGridColumn. * @param integer the speicified position. * @param mixed new item * @throws TInvalidDataTypeException if the item to be inserted is not a TDataGridColumn. */ public function insertAt($index,$item) { if($item instanceof TDataGridColumn) parent::insertAt($index,$item); else throw new TInvalidDataTypeException('datagridcolumncollection_datagridcolumn_required'); } } class TDataGridPagerStyle extends TTableItemStyle { private $_mode=null; private $_nextText=null; private $_prevText=null; private $_buttonCount=null; private $_position=null; private $_visible=null; public function getMode() { return $this->_mode===null?'NextPrev':$this->_mode; } public function setMode($value) { $this->_mode=TPropertyValue::ensureEnum($value,'NextPrev','Numeric'); } public function getNextPageText() { return $this->_nextText===null?'>':$this->_nextText; } public function setNextPageText($value) { $this->_nextText=$value; } public function getPrevPageText() { return $this->_prevText===null?'<':$this->_prevText; } public function setPrevPageText($value) { $this->_prevText=$value; } public function getPageButtonCount() { return $this->_buttonCount===null?10:$this->_buttonCount; } public function setPageButtonCount($value) { if(($value=TPropertyValue::ensureInteger($value))<1) throw new TInvalidDataValueException('datagridpagerstyle_pagebuttoncount_invalid'); $this->_buttonCount=$value; } public function getPosition() { return $this->_position===null?'Bottom':$this->_position; } public function setPosition($value) { $this->_position=TPropertyValue::ensureEnum($value,'Bottom','Top','TopAndBottom'); } public function getVisible() { return $this->_visible===null?true:$this->_visible; } public function setVisible($value) { $this->_visible=TPropertyValue::ensureBoolean($value); } /** * Resets the style to the original empty state. */ public function reset() { parent::reset(); $this->_visible=null; $this->_position=null; $this->_buttonCount=null; $this->_prevText=null; $this->_nextText=null; $this->_mode=null; } /** * Copies the style content from an existing style * This method overrides the parent implementation by * adding additional TDataGridPagerStyle specific attributes. * @param TStyle source style */ public function copyFrom($style) { parent::copyFrom($style); if($style instanceof TDataGridPagerStyle) { $this->_visible=$style->_visible; $this->_position=$style->_position; $this->_buttonCount=$style->_buttonCount; $this->_prevText=$style->_prevText; $this->_nextText=$style->_nextText; $this->_mode=$style->_mode; } } /** * Merges with a style. * If a style field is set in the new style, the current style field * will be overwritten. * This method overrides the parent implementation by * merging with additional TDataGridPagerStyle specific attributes. * @param TStyle the new style */ public function mergeWith($style) { parent::mergeWith($style); if($style instanceof TDataGridPagerStyle) { if($style->_visible!==null) $this->_visible=$style->_visible; if($style->_position!==null) $this->_position=$style->_position; if($style->_buttonCount!==null) $this->_buttonCount=$style->_buttonCount; if($style->_prevText!==null) $this->_prevText=$style->_prevText; if($style->_nextText!==null) $this->_nextText=$style->_nextText; if($style->_mode!==null) $this->_mode=$style->_mode; } } } ?>