From b03d276f8a70675907ec8746c77c780f77f5d11a Mon Sep 17 00:00:00 2001 From: xue <> Date: Tue, 27 Jun 2006 00:31:33 +0000 Subject: Added paging support to all data-bound controls. Updated TPager accordingly. --- framework/Web/UI/WebControls/TDataBoundControl.php | 154 +++++++++- framework/Web/UI/WebControls/TDataGrid.php | 302 +++++--------------- framework/Web/UI/WebControls/TPager.php | 314 ++++++--------------- 3 files changed, 305 insertions(+), 465 deletions(-) (limited to 'framework/Web/UI') diff --git a/framework/Web/UI/WebControls/TDataBoundControl.php b/framework/Web/UI/WebControls/TDataBoundControl.php index 09023b66..798f9155 100644 --- a/framework/Web/UI/WebControls/TDataBoundControl.php +++ b/framework/Web/UI/WebControls/TDataBoundControl.php @@ -12,6 +12,7 @@ Prado::using('System.Web.UI.WebControls.TDataSourceControl'); Prado::using('System.Web.UI.WebControls.TDataSourceView'); +Prado::using('System.Collections.TPagedDataSource'); /** * TDataBoundControl class. @@ -20,7 +21,22 @@ Prado::using('System.Web.UI.WebControls.TDataSourceView'); * data from data sources. It provides basic properties and methods that allow * the derived controls to associate with data sources and retrieve data from them. * - * TBC... + * TBC.... + * + * TDataBoundControl is equipped with paging capabilities. By setting + * {@link setAllowPaging AllowPaging} to true, the input data will be paged + * and only one page of data is actually populated into the data-bound control. + * This saves a lot of memory when dealing with larget datasets. + * + * To specify the number of data items displayed on each page, set + * the {@link setPageSize PageSize} property, and to specify which + * page of data to be displayed, set {@link setCurrentPageIndex CurrentPageIndex}. + * + * When the size of the original data is too big to be loaded all in the memory, + * one can enable custom paging. In custom paging, the total number of data items + * is specified manually via {@link setVirtualItemCount VirtualItemCount}, + * and the data source only needs to contain the current page of data. To enable + * custom paging, set {@link setAllowCustomPaging AllowCustomPaging} to true. * * @author Qiang Xue * @version $Revision: $ $Date: $ @@ -151,6 +167,109 @@ abstract class TDataBoundControl extends TWebControl return $this->_requiresDataBinding; } + /** + * @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 the custom paging is enabled. Defaults to false. + */ + public function getAllowCustomPaging() + { + return $this->getViewState('AllowCustomPaging',false); + } + + /** + * Sets a value indicating whether the custom paging should be enabled. + * When the pager is in custom paging mode, the {@link setVirtualItemCount VirtualItemCount} + * property is used to determine the paging, and the data items in the + * {@link setDataSource DataSource} are considered to be in the current page. + * @param boolean whether the custom paging is enabled + */ + public function setAllowCustomPaging($value) + { + $this->setViewState('AllowCustomPaging',TPropertyValue::ensureBoolean($value),false); + } + + /** + * @return integer the zero-based index of the current page. Defaults to 0. + */ + public function getCurrentPageIndex() + { + return $this->getViewState('CurrentPageIndex',0); + } + + /** + * @param integer the zero-based index of the current page + * @throws TInvalidDataValueException if the value is less than 0 + */ + public function setCurrentPageIndex($value) + { + if(($value=TPropertyValue::ensureInteger($value))<0) + throw new TInvalidDataValueException('databoundcontrol_currentpageindex_invalid',get_class($this)); + $this->setViewState('CurrentPageIndex',$value,0); + } + + /** + * @return integer the number of data items on each page. Defaults to 10. + */ + public function getPageSize() + { + return $this->getViewState('PageSize',10); + } + + /** + * @param integer the number of data items on each page. + * @throws TInvalidDataValueException if the value is less than 1 + */ + public function setPageSize($value) + { + if(($value=TPropertyValue::ensureInteger($value))<1) + throw new TInvalidDataValueException('databoundcontrol_pagesize_invalid',get_class($this)); + $this->setViewState('PageSize',TPropertyValue::ensureInteger($value),10); + } + + /** + * @return integer number of pages of data items available + */ + public function getPageCount() + { + return $this->getViewState('PageCount',1); + } + + /** + * @return integer virtual number of data items in the data source. Defaults to 0. + * @see setAllowCustomPaging + */ + public function getVirtualItemCount() + { + return $this->getViewState('VirtualItemCount',0); + } + + /** + * @param integer virtual number of data items in the data source. + * @throws TInvalidDataValueException if the value is less than 0 + * @see setAllowCustomPaging + */ + public function setVirtualItemCount($value) + { + if(($value=TPropertyValue::ensureInteger($value))<0) + throw new TInvalidDataValueException('databoundcontrol_virtualitemcount_invalid',get_class($this)); + $this->setViewState('VirtualItemCount',$value,0); + } + /** * Sets a value indicating whether a databind call is required by the data bound control. * If true and the control has been prerendered while it uses the data source @@ -184,6 +303,20 @@ abstract class TDataBoundControl extends TWebControl } } + /** + * @return TPagedDataSource creates a paged data source + */ + protected 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; + } + /** * Performs databinding. * This method overrides the parent implementation by calling @@ -197,7 +330,22 @@ abstract class TDataBoundControl extends TWebControl $this->onDataBinding(null); $data=$this->getData(); if($data instanceof Traversable) - $this->performDataBinding($data); + { + if($this->getAllowPaging()) + { + $ds=$this->createPagedDataSource(); + $ds->setDataSource($data); + $this->setViewState('PageCount',$ds->getPageCount()); + if($ds->getCurrentPageIndex()>=$ds->getPageCount()) + throw new TInvalidDataValueException('databoundcontrol_currentpageindex_invalid',get_class($this)); + $this->performDataBinding($ds); + } + else + { + $this->clearViewState('PageCount'); + $this->performDataBinding($data); + } + } $this->setIsDataBound(true); $this->onDataBound(null); } @@ -336,7 +484,7 @@ abstract class TDataBoundControl extends TWebControl else if(($value instanceof Traversable) || $value===null) return $value; else - throw new TInvalidDataTypeException('databoundcontrol_datasource_invalid'); + throw new TInvalidDataTypeException('databoundcontrol_datasource_invalid',get_class($this)); } public function getDataMember() diff --git a/framework/Web/UI/WebControls/TDataGrid.php b/framework/Web/UI/WebControls/TDataGrid.php index 4fe63f52..342a39ac 100644 --- a/framework/Web/UI/WebControls/TDataGrid.php +++ b/framework/Web/UI/WebControls/TDataGrid.php @@ -528,30 +528,6 @@ class TDataGrid extends TBaseDataList implements INamingContainer } } - /** - * @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); - } - /** * @return boolean whether sorting is enabled. Defaults to false. */ @@ -560,14 +536,6 @@ class TDataGrid extends TBaseDataList implements INamingContainer return $this->getViewState('AllowSorting',false); } - /** - * @param boolean whether paging is enabled - */ - public function setAllowPaging($value) - { - $this->setViewState('AllowPaging',TPropertyValue::ensureBoolean($value),false); - } - /** * @param boolean whether sorting is enabled */ @@ -592,74 +560,6 @@ class TDataGrid extends TBaseDataList implements INamingContainer $this->setViewState('AutoGenerateColumns',TPropertyValue::ensureBoolean($value),true); } - /** - * @return integer the zero-based index of the current page. Defaults to 0. - */ - public function getCurrentPageIndex() - { - return $this->getViewState('CurrentPageIndex',0); - } - - /** - * @param integer the zero-based index of the current page - * @throws TInvalidDataValueException if the value is less than 0 - */ - 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 each page. Defaults to 10. - */ - public function getPageSize() - { - return $this->getViewState('PageSize',10); - } - - /** - * @param integer the number of rows displayed within a page - * @throws TInvalidDataValueException if the value is less than 1 - */ - public function setPageSize($value) - { - if(($value=TPropertyValue::ensureInteger($value))<1) - throw new TInvalidDataValueException('datagrid_pagesize_invalid'); - $this->setViewState('PageSize',TPropertyValue::ensureInteger($value),10); - } - - /** - * @return integer number of pages of items available - */ - public function getPageCount() - { - if($this->_pagedDataSource) - return $this->_pagedDataSource->getPageCount(); - else - 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 - * @throws TInvalidDataValueException if the value is less than 0 - */ - 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. */ @@ -955,21 +855,6 @@ class TDataGrid extends TBaseDataList implements INamingContainer } $this->restoreGridFromViewState(); } - $this->clearViewState('ItemCount'); - } - - /** - * @return TPagedDataSource creates a paged data source - */ - 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; } /** @@ -992,42 +877,27 @@ class TDataGrid extends TBaseDataList implements INamingContainer 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))); - if($ds->getCount()===0 && $ds->getCurrentPageIndex()===0 && $this->_emptyTemplate!==null) - { - $this->_emptyTemplate->instantiateIn($this); - $this->_useEmptyTemplate=true; - $this->clearViewState('ItemCount'); - $this->clearViewState('PageCount'); - $this->clearViewState('DataSourceCount'); - return; - } + $allowPaging=$this->getAllowPaging(); + + $itemCount=$this->getViewState('ItemCount',0); + $dsIndex=$this->getViewState('DataSourceIndex',0); $columns=new TList($this->getColumns()); $columns->mergeWith($this->_autoColumns); $items=$this->getItems(); - $items->clear(); if($columns->getCount()) { foreach($columns as $column) $column->initialize(); if($allowPaging) - $this->_topPager=$this->createPager($ds); + $this->_topPager=$this->createPager(); $this->_header=$this->createItemInternal(-1,-1,self::IT_HEADER,false,null,$columns); $selectedIndex=$this->getSelectedItemIndex(); $editIndex=$this->getEditItemIndex(); - $index=0; - $dsIndex=$ds->getAllowPaging()?$ds->getFirstIndexInPage():0; - foreach($ds as $data) + for($index=0;$index<$itemCount;++$index) { if($index===$editIndex) $itemType=self::IT_EDITITEM; @@ -1038,14 +908,17 @@ class TDataGrid extends TBaseDataList implements INamingContainer else $itemType=self::IT_ITEM; $items->add($this->createItemInternal($index,$dsIndex,$itemType,false,null,$columns)); - $index++; $dsIndex++; } $this->_footer=$this->createItemInternal(-1,-1,self::IT_FOOTER,false,null,$columns); if($allowPaging) - $this->_bottomPager=$this->createPager($ds); + $this->_bottomPager=$this->createPager(); + } + if(!$dsIndex && $this->_emptyTemplate!==null) + { + $this->_useEmptyTemplate=true; + $this->_emptyTemplate->instantiateIn($this); } - $this->_pagedDataSource=null; } /** @@ -1060,29 +933,12 @@ class TDataGrid extends TBaseDataList implements INamingContainer $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'); - - if($ds->getCount()===0 && $ds->getCurrentPageIndex()===0 && $this->_emptyTemplate!==null) - { - $this->_useEmptyTemplate=true; - $this->_emptyTemplate->instantiateIn($this); - $this->dataBindChildren(); - $this->clearViewState('ItemCount'); - $this->clearViewState('PageCount'); - $this->clearViewState('DataSourceCount'); - return; - } // get all columns if($this->getAutoGenerateColumns()) { $columns=new TList($this->getColumns()); - $autoColumns=$this->createAutoColumns($ds); + $autoColumns=$this->createAutoColumns($data); $columns->mergeWith($autoColumns); } else @@ -1090,22 +946,23 @@ class TDataGrid extends TBaseDataList implements INamingContainer $items=$this->getItems(); + $index=0; + $allowPaging=$this->getAllowPaging() && ($data instanceof TPagedDataSource); + $dsIndex=$allowPaging?$data->getFirstIndexInPage():0; + $this->setViewState('DataSourceIndex',$dsIndex,0); if($columns->getCount()) { foreach($columns as $column) $column->initialize(); - $allowPaging=$ds->getAllowPaging(); if($allowPaging) - $this->_topPager=$this->createPager($ds); + $this->_topPager=$this->createPager(); $this->_header=$this->createItemInternal(-1,-1,self::IT_HEADER,true,null,$columns); $selectedIndex=$this->getSelectedItemIndex(); $editIndex=$this->getEditItemIndex(); - $index=0; - $dsIndex=$allowPaging?$ds->getFirstIndexInPage():0; - foreach($ds as $key=>$data) + foreach($data as $key=>$row) { if($keyField!=='') - $keys->add($this->getDataFieldValue($data,$keyField)); + $keys->add($this->getDataFieldValue($row,$keyField)); else $keys->add($key); if($index===$editIndex) @@ -1116,24 +973,21 @@ class TDataGrid extends TBaseDataList implements INamingContainer $itemType=self::IT_ALTERNATINGITEM; else $itemType=self::IT_ITEM; - $items->add($this->createItemInternal($index,$dsIndex,$itemType,true,$data,$columns)); + $items->add($this->createItemInternal($index,$dsIndex,$itemType,true,$row,$columns)); $index++; $dsIndex++; } $this->_footer=$this->createItemInternal(-1,-1,self::IT_FOOTER,true,null,$columns); if($allowPaging) - $this->_bottomPager=$this->createPager($ds); - $this->setViewState('ItemCount',$index,0); - $this->setViewState('PageCount',$ds->getPageCount(),0); - $this->setViewState('DataSourceCount',$ds->getDataSourceCount(),0); + $this->_bottomPager=$this->createPager(); } - else + $this->setViewState('ItemCount',$index,0); + if(!$dsIndex && $this->_emptyTemplate!==null) { - $this->clearViewState('ItemCount'); - $this->clearViewState('PageCount'); - $this->clearViewState('DataSourceCount'); + $this->_useEmptyTemplate=true; + $this->_emptyTemplate->instantiateIn($this); + $this->dataBindChildren(); } - $this->_pagedDataSource=null; } /** @@ -1191,10 +1045,10 @@ class TDataGrid extends TBaseDataList implements INamingContainer } } - private function createPager($pagedDataSource) + private function createPager() { $pager=new TDataGridPager($this); - $this->buildPager($pager,$pagedDataSource); + $this->buildPager($pager); $this->onPagerCreated(new TDataGridPagerEventParameter($pager)); $this->getControls()->add($pager); return $pager; @@ -1203,81 +1057,87 @@ class TDataGrid extends TBaseDataList implements INamingContainer /** * Builds the pager content based on pager style. * @param TDataGridPager the container for the pager - * @param TPagedDataSource data source bound to the datagrid */ - protected function buildPager($pager,$dataSource) + protected function buildPager($pager) { switch($this->getPagerStyle()->getMode()) { case 'NextPrev': - $this->buildNextPrevPager($pager,$dataSource); + $this->buildNextPrevPager($pager); break; case 'Numeric': - $this->buildNumericPager($pager,$dataSource); + $this->buildNumericPager($pager); break; } } /** * Creates a pager button. - * @param string button type, LinkButton or PushButton + * Depending on the button type, a TLinkButton or a TButton may be created. + * If it is enabled (clickable), its command name and parameter will also be set. + * Derived classes may override this method to create additional types of buttons, such as TImageButton. + * @param string button type, either LinkButton or PushButton * @param boolean whether the button should be enabled + * @param string caption of the button + * @param string CommandName corresponding to the OnCommand event of the button + * @param string CommandParameter corresponding to the OnCommand event of the button * @return mixed the button instance */ - protected function createPagerButton($buttonType,$enabled) + protected function createPagerButton($buttonType,$enabled,$text,$commandName,$commandParameter) { if($buttonType==='LinkButton') { - return $enabled?new TLinkButton:new TLabel; + if($enabled) + $button=new TLinkButton; + else + { + $button=new TLabel; + $button->setText($text); + return $button; + } } else { $button=new TButton; if(!$enabled) $button->setEnabled(false); - return $button; } + $button->setText($text); + $button->setCommandName($commandName); + $button->setCommandParameter($commandParameter); + $button->setCausesValidation(false); + return $button; } /** * Builds a next-prev pager * @param TDataGridPager the container for the pager - * @param TPagedDataSource data source bound to the datagrid */ - protected function buildNextPrevPager($pager,$dataSource) + protected function buildNextPrevPager($pager) { $style=$this->getPagerStyle(); $buttonType=$style->getButtonType(); $controls=$pager->getControls(); - if($dataSource->getIsFirstPage()) + $currentPageIndex=$this->getCurrentPageIndex(); + if($currentPageIndex===0) { - $label=$this->createPagerButton($buttonType,false); - $label->setText($style->getPrevPageText()); + $label=$this->createPagerButton($buttonType,false,$style->getPrevPageText(),'',''); $controls->add($label); } else { - $button=$this->createPagerButton($buttonType,true); - $button->setText($style->getPrevPageText()); - $button->setCommandName(self::CMD_PAGE); - $button->setCommandParameter(self::CMD_PAGE_PREV); - $button->setCausesValidation(false); + $button=$this->createPagerButton($buttonType,true,$style->getPrevPageText(),self::CMD_PAGE,self::CMD_PAGE_PREV); $controls->add($button); } - $controls->add(' '); - if($dataSource->getIsLastPage()) + $controls->add("\n"); + if($currentPageIndex===$this->getPageCount()-1) { - $label=$this->createPagerButton($buttonType,false); - $label->setText($style->getNextPageText()); + $label=$this->createPagerButton($buttonType,false,$style->getNextPageText(),'',''); $controls->add($label); } else { - $button=$this->createPagerButton($buttonType,true); - $button->setText($style->getNextPageText()); - $button->setCommandName(self::CMD_PAGE); - $button->setCommandParameter(self::CMD_PAGE_NEXT); - $button->setCausesValidation(false); + $button=$this->createPagerButton($buttonType,true,$style->getNextPageText(),self::CMD_PAGE,self::CMD_PAGE_NEXT); $controls->add($button); } } @@ -1285,15 +1145,14 @@ class TDataGrid extends TBaseDataList implements INamingContainer /** * Builds a numeric pager * @param TDataGridPager the container for the pager - * @param TPagedDataSource data source bound to the datagrid */ - protected function buildNumericPager($pager,$dataSource) + protected function buildNumericPager($pager) { $style=$this->getPagerStyle(); $buttonType=$style->getButtonType(); $controls=$pager->getControls(); - $pageCount=$dataSource->getPageCount(); - $pageIndex=$dataSource->getCurrentPageIndex()+1; + $pageCount=$this->getPageCount(); + $pageIndex=$this->getCurrentPageIndex()+1; $maxButtonCount=$style->getPageButtonCount(); $buttonCount=$maxButtonCount>$pageCount?$pageCount:$maxButtonCount; $startPageIndex=1; @@ -1312,51 +1171,40 @@ class TDataGrid extends TBaseDataList implements INamingContainer if($startPageIndex>1) { - $button=$this->createPagerButton($buttonType,true); - $button->setText($style->getPrevPageText()); - $button->setCommandName(self::CMD_PAGE); - $button->setCommandParameter($startPageIndex-1); - $button->setCausesValidation(false); + $prevPageIndex=$startPageIndex-1; + $button=$this->createPagerButton($buttonType,true,$style->getPrevPageText(),self::CMD_PAGE,"$prevPageIndex"); $controls->add($button); - $controls->add(' '); + $controls->add("\n"); } for($i=$startPageIndex;$i<=$endPageIndex;++$i) { if($i===$pageIndex) { - $label=$this->createPagerButton($buttonType,false); - $label->setText("$i"); + $label=$this->createPagerButton($buttonType,false,"$i",'',''); $controls->add($label); } else { - $button=$this->createPagerButton($buttonType,true); - $button->setText("$i"); - $button->setCommandName(self::CMD_PAGE); - $button->setCommandParameter($i); - $button->setCausesValidation(false); + $button=$this->createPagerButton($buttonType,true,"$i",self::CMD_PAGE,"$i"); $controls->add($button); } if($i<$endPageIndex) - $controls->add(' '); + $controls->add("\n"); } if($pageCount>$endPageIndex) { - $controls->add(' '); - $button=$this->createPagerButton($buttonType,true); - $button->setText($style->getNextPageText()); - $button->setCommandName(self::CMD_PAGE); - $button->setCommandParameter($endPageIndex+1); - $button->setCausesValidation(false); + $controls->add("\n"); + $nextPageIndex=$endPageIndex+1; + $button=$this->createPagerButton($buttonType,true,$style->getNextPageText(),self::CMD_PAGE,"$nextPageIndex"); $controls->add($button); } } /** * Automatically generates datagrid columns based on datasource schema - * @param TPagedDataSource data source bound to the datagrid + * @param Traversable data source bound to the datagrid * @return TDataGridColumnCollection */ protected function createAutoColumns($dataSource) diff --git a/framework/Web/UI/WebControls/TPager.php b/framework/Web/UI/WebControls/TPager.php index f802e8d7..0401c4f2 100644 --- a/framework/Web/UI/WebControls/TPager.php +++ b/framework/Web/UI/WebControls/TPager.php @@ -10,27 +10,18 @@ * @package System.Web.UI.WebControls */ -Prado::using('System.Web.UI.WebControls.TDataBoundControl'); -Prado::using('System.Web.UI.WebControls.TPanelStyle'); -Prado::using('System.Collections.TPagedDataSource'); -Prado::using('System.Collections.TDummyDataSource'); - /** * TPager class. * - * TPager creates a pager that controls the paging of the data populated - * to a data-bound control specified by {@link setControlToPaginate ControlToPaginate}. - * To specify the number of data items displayed on each page, set - * the {@link setPageSize PageSize} property, and to specify which - * page of data to be displayed, set {@link setCurrentPageIndex CurrentPageIndex}. - * - * When the size of the original data is too big to be loaded all in the memory, - * one can enable custom paging. In custom paging, the total number of data items - * is specified manually via {@link setVirtualItemCount VirtualItemCount}, and the data source - * only needs to contain the current page of data. To enable custom paging, - * set {@link setAllowCustomPaging AllowCustomPaging} to true. + * TPager creates a pager that provides UI for end-users to interactively + * specify which page of data to be rendered in a {@link TDataBoundControl}-derived control, + * such as {@link TDataList}, {@link TRepeater}, {@link TCheckBoxList}, etc. + * The target data-bound control is specified by {@link setControlToPaginate ControlToPaginate}, + * which must be the ID path of the target control reaching from the pager's + * naming container. Note, the target control must have its {@link TDataBoundControl::setAllowPaging AllowPaging} + * set to true. * - * TPager can be in one of three {@link setMode Mode}: + * TPager can display three different UIs, specified via {@link setMode Mode}: * - NextPrev: a next page and a previous page button are rendered. * - Numeric: a list of page index buttons are rendered. * - List: a dropdown list of page indices are rendered. @@ -39,27 +30,17 @@ Prado::using('System.Collections.TDummyDataSource'); * the end-user interacts with it and specifies a new page (e.g. clicking * on a page button that leads to a new page.) The new page index may be obtained * from the event parameter's property {@link TPagerPageChangedEventParameter::getNewPageIndex NewPageIndex}. + * Normally, in the event handler, one can set the {@link TDataBoundControl::getCurrentPageIndex CurrentPageIndex} + * to this new page index so that the new page of data is rendered. * - * When multiple pagers are associated with the same data-bound control, - * these pagers will do synchronization among each other so that the interaction - * with one pager will automatically update the UI of the other relevant pagers. - * - * The following example shows a typical usage of TPager: - * - * $pager->ControlToPaginate="Path.To.Control"; - * $pager->DataSource=$data; - * $pager->dataBind(); - * - * Note, the data is assigned to the pager and dataBind() is invoked against the pager. - * Without the pager, one has to set datasource for the target control and call - * its dataBind() directly. + * Multiple pagers can be associated with the same data-bound control. * * @author Qiang Xue * @version $Revision: $ $Date: $ * @package System.Web.UI.WebControls * @since 3.0.2 */ -class TPager extends TDataBoundControl implements INamingContainer +class TPager extends TWebControl implements INamingContainer { /** * Command name that TPager understands. @@ -70,36 +51,6 @@ class TPager extends TDataBoundControl implements INamingContainer const CMD_PAGE_FIRST='First'; const CMD_PAGE_LAST='Last'; - /** - * @var array list of all pagers, used to synchronize their appearance - */ - static private $_pagers=array(); - - /** - * Registers the pager itself to a global list. - * This method overrides the parent implementation and is invoked during - * OnInit control lifecycle. - * @param mixed event parameter - */ - public function onInit($param) - { - parent::onInit($param); - self::$_pagers[]=$this; - } - - /** - * Unregisters the pager from a global list. - * This method overrides the parent implementation and is invoked during - * OnUnload control lifecycle. - * @param mixed event parameter - */ - public function onUnload($param) - { - parent::onUnload($param); - if(($index=array_search($this,self::$_pagers,true))!==false) - unset(self::$_pagers[$index]); - } - /** * Restores the pager state. * This method overrides the parent implementation and is invoked when @@ -108,10 +59,11 @@ class TPager extends TDataBoundControl implements INamingContainer public function loadState() { parent::loadState(); - if(!$this->getEnableViewState(true)) - return; - if(!$this->getIsDataBound()) - $this->restoreFromViewState(); + if($this->getEnableViewState(true)) + { + $this->getControls()->clear(); + $this->buildPager(); + } } /** @@ -133,107 +85,6 @@ class TPager extends TDataBoundControl implements INamingContainer $this->setViewState('ControlToPaginate',$value,''); } - /** - * @return integer the zero-based index of the current page. Defaults to 0. - */ - public function getCurrentPageIndex() - { - return $this->getViewState('CurrentPageIndex',0); - } - - /** - * @param integer the zero-based index of the current page - * @throws TInvalidDataValueException if the value is less than 0 - */ - public function setCurrentPageIndex($value) - { - if(($value=TPropertyValue::ensureInteger($value))<0) - throw new TInvalidDataValueException('pager_currentpageindex_invalid'); - $this->setViewState('CurrentPageIndex',$value,0); - } - - /** - * @return integer the number of data items on each page. Defaults to 10. - */ - public function getPageSize() - { - return $this->getViewState('PageSize',10); - } - - /** - * @param integer the number of data items on each page. - * @throws TInvalidDataValueException if the value is less than 1 - */ - public function setPageSize($value) - { - if(($value=TPropertyValue::ensureInteger($value))<1) - throw new TInvalidDataValueException('pager_pagesize_invalid'); - $this->setViewState('PageSize',TPropertyValue::ensureInteger($value),10); - } - - /** - * @return integer number of pages - */ - public function getPageCount() - { - if(($count=$this->getItemCount())<1) - return 1; - else - { - $pageSize=$this->getPageSize(); - return (int)(($count+$pageSize-1)/$pageSize); - } - } - - /** - * @return boolean whether the custom paging is enabled. Defaults to false. - */ - public function getAllowCustomPaging() - { - return $this->getViewState('AllowCustomPaging',false); - } - - /** - * Sets a value indicating whether the custom paging should be enabled. - * When the pager is in custom paging mode, the {@link setVirtualItemCount VirtualItemCount} - * property is used to determine the paging, and the data items in the - * {@link setDataSource DataSource} are considered to be in the current page. - * @param boolean whether the custom paging is enabled - */ - public function setAllowCustomPaging($value) - { - $this->setViewState('AllowCustomPaging',TPropertyValue::ensureBoolean($value),false); - } - - /** - * @return integer virtual number of data items in the data source. Defaults to 0. - * @see setAllowCustomPaging - */ - public function getVirtualItemCount() - { - return $this->getViewState('VirtualItemCount',0); - } - - /** - * @param integer virtual number of data items in the data source. - * @throws TInvalidDataValueException if the value is less than 0 - * @see setAllowCustomPaging - */ - public function setVirtualItemCount($value) - { - if(($value=TPropertyValue::ensureInteger($value))<0) - throw new TInvalidDataValueException('pager_virtualitemcount_invalid'); - $this->setViewState('VirtualItemCount',$value,0); - } - - /** - * @return integer total number of items in the datasource. - */ - public function getItemCount() - { - return $this->getViewState('ItemCount',0); - } - /** * @return string pager mode. Defaults to 'NextPrev'. */ @@ -350,38 +201,57 @@ class TPager extends TDataBoundControl implements INamingContainer } /** - * @return TPagedDataSource creates a paged data source + * @return integer the zero-based index of the current page. Defaults to 0. */ - private function createPagedDataSource() + public function getCurrentPageIndex() { - $ds=new TPagedDataSource; - $ds->setAllowPaging(true); - $customPaging=$this->getAllowCustomPaging(); - $ds->setAllowCustomPaging($customPaging); - $ds->setCurrentPageIndex($this->getCurrentPageIndex()); - $ds->setPageSize($this->getPageSize()); - if($customPaging) - $ds->setVirtualItemCount($this->getVirtualItemCount()); - return $ds; + return $this->getViewState('CurrentPageIndex',0); } /** - * Removes the existing child controls. + * @param integer the zero-based index of the current page + * @throws TInvalidDataValueException if the value is less than 0 */ - protected function reset() + protected function setCurrentPageIndex($value) { - $this->getControls()->clear(); + if(($value=TPropertyValue::ensureInteger($value))<0) + throw new TInvalidDataValueException('pager_currentpageindex_invalid'); + $this->setViewState('CurrentPageIndex',$value,0); } /** - * Restores the pager from viewstate. + * @return integer number of pages of data items available */ - protected function restoreFromViewState() + public function getPageCount() { - $this->reset(); - $ds=$this->createPagedDataSource(); - $ds->setDataSource(new TDummyDataSource($this->getItemCount())); - $this->buildPager($ds); + return $this->getViewState('PageCount',0); + } + + /** + * @param integer number of pages of data items available + * @throws TInvalidDataValueException if the value is less than 0 + */ + protected function setPageCount($value) + { + if(($value=TPropertyValue::ensureInteger($value))<0) + throw new TInvalidDataValueException('pager_pagecount_invalid'); + $this->setViewState('PageCount',$value,0); + } + + /** + * @return boolean whether the current page is the first page Defaults to false. + */ + public function getIsFirstPage() + { + return $this->getCurrentPageIndex()===0; + } + + /** + * @return boolean whether the current page is the last page + */ + public function getIsLastPage() + { + return $this->getCurrentPageIndex()===$this->getPageCount()-1; } /** @@ -390,66 +260,43 @@ class TPager extends TDataBoundControl implements INamingContainer * You may override this function to provide your own way of data population. * @param Traversable the bound data */ - protected function performDataBinding($data) + public function onPreRender($param) { - $this->reset(); + parent::onPreRender($param); $controlID=$this->getControlToPaginate(); if(($targetControl=$this->getNamingContainer()->findControl($controlID))===null || !($targetControl instanceof TDataBoundControl)) throw new TConfigurationException('pager_controltopaginate_invalid',$controlID); - $ds=$this->createPagedDataSource(); - $ds->setDataSource($this->getDataSource()); - $this->setViewState('ItemCount',$ds->getDataSourceCount()); - - $this->buildPager($ds); - $this->synchronizePagers($targetControl,$ds); - - $targetControl->setDataSource($ds); - $targetControl->dataBind(); - } - - /** - * Synchronizes the state of all pagers who have the same {@link getControlToPaginate ControlToPaginate}. - * @param TDataBoundControl the control whose content is to be paginated - * @param TPagedDataSource the paged data source associated with the pager - */ - protected function synchronizePagers($targetControl,$dataSource) - { - foreach(self::$_pagers as $pager) + if($targetControl->getAllowPaging() && $targetControl->getPageCount()>1) { - if($pager!==$this && $pager->getNamingContainer()->findControl($pager->getControlToPaginate())===$targetControl) - { - $pager->reset(); - $pager->setCurrentPageIndex($dataSource->getCurrentPageIndex()); - $customPaging=$dataSource->getAllowCustomPaging(); - $pager->setAllowCustomPaging($customPaging); - $pager->setViewState('ItemCount',$dataSource->getDataSourceCount()); - if($customPaging) - $pager->setVirtualItemCount($dataSource->getVirtualItemCount()); - $pager->buildPager($dataSource); - } + $this->setVisible(true); + $this->getControls()->clear(); + $this->setPageCount($targetControl->getPageCount()); + $this->setCurrentPageIndex($targetControl->getCurrentPageIndex()); + $this->buildPager(); } + else + $this->setVisible(false); } /** * Builds the pager content based on the pager mode. * Current implementation includes building 'NextPrev', 'Numeric' and 'List' pagers. * Derived classes may override this method to provide additional pagers. - * @param TPagedDataSource data source bound to the target control */ - protected function buildPager($dataSource) + protected function buildPager() { switch($this->getMode()) { case 'NextPrev': - $this->buildNextPrevPager($dataSource); + $this->buildNextPrevPager(); break; case 'Numeric': - $this->buildNumericPager($dataSource); + $this->buildNumericPager(); break; case 'List': - $this->buildListPager($dataSource); + $this->buildListPager(); break; } } @@ -494,13 +341,12 @@ class TPager extends TDataBoundControl implements INamingContainer /** * Builds a next-prev pager - * @param TPagedDataSource data source bound to the pager */ - protected function buildNextPrevPager($dataSource) + protected function buildNextPrevPager() { $buttonType=$this->getButtonType(); $controls=$this->getControls(); - if($dataSource->getIsFirstPage()) + if($this->getIsFirstPage()) { if(($text=$this->getFirstPageText())!=='') { @@ -523,7 +369,7 @@ class TPager extends TDataBoundControl implements INamingContainer $controls->add($button); } $controls->add("\n"); - if($dataSource->getIsLastPage()) + if($this->getIsLastPage()) { $label=$this->createPagerButton($buttonType,false,$this->getNextPageText(),'',''); $controls->add($label); @@ -549,14 +395,13 @@ class TPager extends TDataBoundControl implements INamingContainer /** * Builds a numeric pager - * @param TPagedDataSource data source bound to the pager */ - protected function buildNumericPager($dataSource) + protected function buildNumericPager() { $buttonType=$this->getButtonType(); $controls=$this->getControls(); - $pageCount=$dataSource->getPageCount(); - $pageIndex=$dataSource->getCurrentPageIndex()+1; + $pageCount=$this->getPageCount(); + $pageIndex=$this->getCurrentPageIndex()+1; $maxButtonCount=$this->getPageButtonCount(); $buttonCount=$maxButtonCount>$pageCount?$pageCount:$maxButtonCount; $startPageIndex=1; @@ -620,15 +465,14 @@ class TPager extends TDataBoundControl implements INamingContainer /** * Builds a dropdown list pager - * @param TPagedDataSource data source bound to the pager */ - protected function buildListPager($dataSource) + protected function buildListPager() { $list=new TDropDownList; $this->getControls()->add($list); - $list->setDataSource(range(1,$dataSource->getPageCount())); + $list->setDataSource(range(1,$this->getPageCount())); $list->dataBind(); - $list->setSelectedIndex($dataSource->getCurrentPageIndex()); + $list->setSelectedIndex($this->getCurrentPageIndex()); $list->setAutoPostBack(true); $list->attachEventHandler('OnSelectedIndexChanged',array($this,'listIndexChanged')); } -- cgit v1.2.3