diff options
Diffstat (limited to 'framework/Web/UI/WebControls')
| -rw-r--r-- | framework/Web/UI/WebControls/TDataBoundControl.php | 154 | ||||
| -rw-r--r-- | framework/Web/UI/WebControls/TDataGrid.php | 302 | ||||
| -rw-r--r-- | framework/Web/UI/WebControls/TPager.php | 314 | 
3 files changed, 305 insertions, 465 deletions
| 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 <qiang.xue@gmail.com>
   * @version $Revision: $  $Date: $
 @@ -152,6 +168,109 @@ abstract class TDataBoundControl extends TWebControl  	}
  	/**
 +	 * @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
  	 * specified by {@link setDataSourceID}, a databind call will be called by this method.
 @@ -185,6 +304,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
  	 * {@link performSelect} which fetches data from data source and does
 @@ -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 @@ -529,30 +529,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.
  	 */
  	public function getAllowSorting()
 @@ -561,14 +537,6 @@ class TDataGrid extends TBaseDataList implements INamingContainer  	}
  	/**
 -	 * @param boolean whether paging is enabled
 -	 */
 -	public function setAllowPaging($value)
 -	{
 -		$this->setViewState('AllowPaging',TPropertyValue::ensureBoolean($value),false);
 -	}
 -
 -	/**
  	 * @param boolean whether sorting is enabled
  	 */
  	public function setAllowSorting($value)
 @@ -593,74 +561,6 @@ class TDataGrid extends TBaseDataList implements INamingContainer  	}
  	/**
 -	 * @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.
  	 */
  	public function getShowHeader()
 @@ -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:
 - * <code>
 - * $pager->ControlToPaginate="Path.To.Control";
 - * $pager->DataSource=$data;
 - * $pager->dataBind();
 - * </code>
 - * 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 <qiang.xue@gmail.com>
   * @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.
 @@ -71,36 +52,6 @@ class TPager extends TDataBoundControl implements INamingContainer  	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
  	 * the control is loading persistent state.
 @@ -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();
 +		}
  	}
  	/**
 @@ -134,107 +86,6 @@ class TPager extends TDataBoundControl implements INamingContainer  	}
  	/**
 -	 * @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'.
  	 */
  	public function getMode()
 @@ -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'));
  	}
 | 
