summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitattributes10
-rw-r--r--HISTORY4
-rw-r--r--demos/quickstart/protected/application.xml3
-rw-r--r--demos/quickstart/protected/comments/QuickStartComments.php34
-rw-r--r--demos/quickstart/protected/index/api/_0.cfsbin378721 -> 0 bytes
-rw-r--r--demos/quickstart/protected/index/api/deletablebin4 -> 0 bytes
-rw-r--r--demos/quickstart/protected/index/api/segmentsbin27 -> 0 bytes
-rw-r--r--demos/quickstart/protected/pages/Controls/Pager.page38
-rw-r--r--demos/quickstart/protected/pages/Controls/Samples/TPager/Sample1.page77
-rw-r--r--demos/quickstart/protected/pages/Controls/Samples/TPager/Sample1.php82
-rw-r--r--demos/quickstart/protected/pages/Controls/Standard.page4
-rw-r--r--framework/Exceptions/messages.txt22
-rw-r--r--framework/Web/UI/WebControls/TDataBoundControl.php154
-rw-r--r--framework/Web/UI/WebControls/TDataGrid.php304
-rw-r--r--framework/Web/UI/WebControls/TDataGridColumn.php23
-rw-r--r--framework/Web/UI/WebControls/TDataList.php2
-rw-r--r--framework/Web/UI/WebControls/TMultiView.php5
-rw-r--r--framework/Web/UI/WebControls/TPager.php314
-rw-r--r--tests/FunctionalTests/quickstart/Controls/PagerTestCase.php58
-rw-r--r--tests/FunctionalTests/tickets/protected/pages/Ticket239.page19
-rw-r--r--tests/FunctionalTests/tickets/protected/pages/Ticket239.php16
-rw-r--r--tests/FunctionalTests/tickets/tests/Ticket239TestCase.php55
22 files changed, 727 insertions, 497 deletions
diff --git a/.gitattributes b/.gitattributes
index 3c3ac2f0..487302b1 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -749,9 +749,6 @@ demos/quickstart/protected/index/Zend/Search/Lucene/Storage/File.php -text
demos/quickstart/protected/index/Zend/Search/Lucene/Storage/File/Filesystem.php -text
demos/quickstart/protected/index/Zend/Search/TODO.txt -text
demos/quickstart/protected/index/ZendSearch.php -text
-demos/quickstart/protected/index/api/_0.cfs -text
-demos/quickstart/protected/index/api/deletable -text
-demos/quickstart/protected/index/api/segments -text
demos/quickstart/protected/index/quickstart/_0.cfs -text
demos/quickstart/protected/index/quickstart/deletable -text
demos/quickstart/protected/index/quickstart/segments -text
@@ -834,6 +831,7 @@ demos/quickstart/protected/pages/Controls/List.page -text
demos/quickstart/protected/pages/Controls/Literal.page -text
demos/quickstart/protected/pages/Controls/MultiView.page -text
demos/quickstart/protected/pages/Controls/NewControl.page -text
+demos/quickstart/protected/pages/Controls/Pager.page -text
demos/quickstart/protected/pages/Controls/Panel.page -text
demos/quickstart/protected/pages/Controls/PlaceHolder.page -text
demos/quickstart/protected/pages/Controls/RadioButton.page -text
@@ -907,6 +905,8 @@ demos/quickstart/protected/pages/Controls/Samples/TListBox/Home.php -text
demos/quickstart/protected/pages/Controls/Samples/TLiteral/Home.page -text
demos/quickstart/protected/pages/Controls/Samples/TMultiView/Home.page -text
demos/quickstart/protected/pages/Controls/Samples/TMultiView/Home.php -text
+demos/quickstart/protected/pages/Controls/Samples/TPager/Sample1.page -text
+demos/quickstart/protected/pages/Controls/Samples/TPager/Sample1.php -text
demos/quickstart/protected/pages/Controls/Samples/TPanel/Home.page -text
demos/quickstart/protected/pages/Controls/Samples/TPanel/Home.php -text
demos/quickstart/protected/pages/Controls/Samples/TPanel/hello_world.gif -text
@@ -1753,6 +1753,7 @@ tests/FunctionalTests/quickstart/Controls/LabelTestCase.php -text
tests/FunctionalTests/quickstart/Controls/LinkButtonTestCase.php -text
tests/FunctionalTests/quickstart/Controls/ListBoxTestCase.php -text
tests/FunctionalTests/quickstart/Controls/MultiViewTestCase.php -text
+tests/FunctionalTests/quickstart/Controls/PagerTestCase.php -text
tests/FunctionalTests/quickstart/Controls/PanelTestCase.php -text
tests/FunctionalTests/quickstart/Controls/RadioButtonListTestCase.php -text
tests/FunctionalTests/quickstart/Controls/RadioButtonTestCase.php -text
@@ -1814,6 +1815,8 @@ tests/FunctionalTests/tickets/protected/pages/Ticket191.page -text
tests/FunctionalTests/tickets/protected/pages/Ticket191.php -text
tests/FunctionalTests/tickets/protected/pages/Ticket21.page -text
tests/FunctionalTests/tickets/protected/pages/Ticket21.php -text
+tests/FunctionalTests/tickets/protected/pages/Ticket239.page -text
+tests/FunctionalTests/tickets/protected/pages/Ticket239.php -text
tests/FunctionalTests/tickets/protected/pages/Ticket27.page -text
tests/FunctionalTests/tickets/protected/pages/Ticket28.page -text
tests/FunctionalTests/tickets/protected/pages/Ticket28.php -text
@@ -1832,6 +1835,7 @@ tests/FunctionalTests/tickets/tests/Ticket163TestCase.php -text
tests/FunctionalTests/tickets/tests/Ticket169TestCase.php -text
tests/FunctionalTests/tickets/tests/Ticket191TestCase.php -text
tests/FunctionalTests/tickets/tests/Ticket21TestCase.php -text
+tests/FunctionalTests/tickets/tests/Ticket239TestCase.php -text
tests/FunctionalTests/tickets/tests/Ticket27TestCase.php -text
tests/FunctionalTests/tickets/tests/Ticket28TestCase.php -text
tests/FunctionalTests/tickets/tests/Ticket54TestCase.php -text
diff --git a/HISTORY b/HISTORY
index 477d1a30..f1fca4fa 100644
--- a/HISTORY
+++ b/HISTORY
@@ -15,6 +15,7 @@ BUG: Ticket#182 - List and validator controls cause problem in child classes (Qi
BUG: Ticket#191 - Duplicated postbacks occur when using TButton with validators (Qiang)
BUG: Ticket#213 - PRADO Requirements Checker charset error (Qiang)
BUG: Ticket#234 - Postback target could be out of date (Qiang)
+BUG: Ticket#239 - Ondeactivate handler for the first View of MultiView is always fired (Qiang)
BUG: Ticket#244 - redirect() needs absolute URL (Qiang)
BUG: Ticket#245 - getIsSecureConnection() is not working correctly (Qiang)
CHG: ensureChildControls() is now invoked in TControl::initRecursive (Qiang)
@@ -24,9 +25,12 @@ ENH: Ticket#206 - Added OnValidate, OnError, OnSuccess events to validators (Qia
ENH: Ticket#230 - Added TDataList.EmptyTemplate property (Qiang)
ENH: Ticket#231 - Added TButton.ButtonType property to allow reset button (Qiang)
ENH: Ticket#232 - Allow <%# %> and <%= %> embedded within property values (Qiang)
+ENH: Ticket#256 - Datagrid columns can now be accessed via IDs (Qiang)
+ENH: Ticket#257 - OnSelectedIndexChanged event of TDataList and TDataGrid now passes the original command parameter (Qiang)
ENH: TRepeater, TDataList and TDataGrid will store data indices in DataKeys if DataKeyField is not set. (Qiang)
ENH: Added TPageService.BasePageClass property (Qiang)
ENH: Added TDataGrid.EmptyTemplate property (Qiang)
+ENH: Added paging feature to all TDataBoundControl-derived controls (Qiang)
NEW: Added TPager (Qiang)
Version 3.0.1 June 4, 2006
diff --git a/demos/quickstart/protected/application.xml b/demos/quickstart/protected/application.xml
index 1765230b..79ee4529 100644
--- a/demos/quickstart/protected/application.xml
+++ b/demos/quickstart/protected/application.xml
@@ -13,9 +13,6 @@
<module id="quickstart_search"
class="Application.index.ZendSearch"
IndexDataDirectory="Application.index.quickstart" />
- <module id="api_search"
- class="Application.index.ZendSearch"
- IndexDataDirectory="Application.index.api" />
</modules>
<paths>
<using namespace="Application.controls.*" />
diff --git a/demos/quickstart/protected/comments/QuickStartComments.php b/demos/quickstart/protected/comments/QuickStartComments.php
index c138bd94..7b504caf 100644
--- a/demos/quickstart/protected/comments/QuickStartComments.php
+++ b/demos/quickstart/protected/comments/QuickStartComments.php
@@ -18,7 +18,7 @@ class QuickStartComments
* @var sqlite connection.
*/
private $_connection;
-
+
/**
* Sets the sqlite comment database file.
*/
@@ -26,7 +26,7 @@ class QuickStartComments
{
$this->_database = realpath(dirname(__FILE__).'/comments.db');
}
-
+
/**
* Closed the database connection.
*/
@@ -35,7 +35,7 @@ class QuickStartComments
if(!is_null($this->_connection))
sqlite_close($this->_connection);
}
-
+
/**
* @return resource sqlite database connection.
*/
@@ -45,7 +45,7 @@ class QuickStartComments
$this->_connection = sqlite_open($this->_database);
return $this->_connection;
}
-
+
/**
* Quote database input data.
*/
@@ -53,7 +53,7 @@ class QuickStartComments
{
return sqlite_escape_string($value);
}
-
+
/**
* Executes an sqlite query.
* @param string SQL
@@ -63,7 +63,7 @@ class QuickStartComments
{
return sqlite_query($this->getConnection(), $sql);
}
-
+
/**
* Returns a row from the sqlite result.
* @param resource sqlite result
@@ -76,7 +76,7 @@ class QuickStartComments
else
return false;
}
-
+
/**
* Fetch all the records for given SQL query.
* @param string SQL query.
@@ -90,7 +90,7 @@ class QuickStartComments
$rows[] = $row;
return $rows;
}
-
+
/**
* Returns all the comments for a given page.
* @param string specific page comments
@@ -99,10 +99,10 @@ class QuickStartComments
public function getComments($pageID)
{
$page = $this->quote($pageID);
- $sql = "SELECT * FROM comments WHERE page=\"$page\" AND approved = 1 ORDER BY date_added ASC";
+ $sql = "SELECT * FROM comments WHERE page='$page' AND approved = 1 ORDER BY date_added ASC";
return $this->fetchAll($sql);
}
-
+
/**
* Adds a new comment for moderation.
* @param string ID of the page to comment belongs
@@ -117,11 +117,11 @@ class QuickStartComments
$date_added = time();
$sql = <<<EOD
INSERT INTO comments(page, email, comment, date_added)
- VALUES ("$page", "$email", "$comment", "$date_added")
+ VALUES ('$page', '$email', '$comment', '$date_added')
EOD;
return $this->query($sql);
}
-
+
/**
* Update an existing comment.
* @param string comment ID
@@ -136,13 +136,13 @@ EOD;
$comment = $this->quote($content);
$page = $this->quote($page);
$sql = <<<EOD
- UPDATE comments SET
- email = "$email", comment = "$comment", page = "$page"
+ UPDATE comments SET
+ email = '$email', comment = '$comment', page = '$page'
WHERE id = $ID;
EOD;
$this->query($sql);
}
-
+
/**
* Delete a comment.
* @param string comment ID
@@ -152,7 +152,7 @@ EOD;
$ID = intval($commentID);
$this->query("DELETE FROM comments WHERE id=$ID");
}
-
+
/**
* @return array all the quequed comments.
*/
@@ -160,7 +160,7 @@ EOD;
{
return $this->fetchAll("SELECT * FROM comments WHERE approved != 1");
}
-
+
/**
* Approve a quequed comment.
* @param string comment ID.
diff --git a/demos/quickstart/protected/index/api/_0.cfs b/demos/quickstart/protected/index/api/_0.cfs
deleted file mode 100644
index b5eb219b..00000000
--- a/demos/quickstart/protected/index/api/_0.cfs
+++ /dev/null
Binary files differ
diff --git a/demos/quickstart/protected/index/api/deletable b/demos/quickstart/protected/index/api/deletable
deleted file mode 100644
index 593f4708..00000000
--- a/demos/quickstart/protected/index/api/deletable
+++ /dev/null
Binary files differ
diff --git a/demos/quickstart/protected/index/api/segments b/demos/quickstart/protected/index/api/segments
deleted file mode 100644
index da225127..00000000
--- a/demos/quickstart/protected/index/api/segments
+++ /dev/null
Binary files differ
diff --git a/demos/quickstart/protected/pages/Controls/Pager.page b/demos/quickstart/protected/pages/Controls/Pager.page
new file mode 100644
index 00000000..3c74d0de
--- /dev/null
+++ b/demos/quickstart/protected/pages/Controls/Pager.page
@@ -0,0 +1,38 @@
+<com:TContent ID="body" >
+
+<h1>TPager</h1>
+<com:DocLink ClassPath="System.Web.UI.WebControls.TPager" />
+
+<p>
+<tt>TPager</tt> creates a pager that provides UI for end-users to interactively specify which page of data to be rendered in a <tt>TDataBoundControl</tt>-derived control, such as <tt>TDataList</tt>, <tt>TRepeater</tt>, <tt>TCheckBoxList</tt>, etc. The target data-bound control is specified by the <tt>ControlToPaginate</tt> property, which must be the ID path of the target control reaching from the pager's naming container.
+</p>
+
+<p>
+Note, the target data-bound control must have its <tt>AllowPaging</tt> set to true. Otherwise the pager will be invisible. Also, in case when there is only one page of data available, the pager will also be invisible.
+</p>
+
+<p>
+<tt>TPager</tt> can display one of the following three types of user interface, specified via its <tt>Mode</tt> property:
+</p>
+<ul>
+<li><tt>NextPrev</tt> - a next page and a previous page button are rendered on each page.</li>
+<li><tt>Numeric</tt> - a list of page index buttons are rendered.</li>
+<li><tt>List</tt> - a dropdown list of page indices is rendered.</li>
+</ul>
+<p>
+These user interfaces may be further customized by configuring the following properties
+</p>
+<ul>
+<li><tt>NextPageText</tt> and <tt>PrevPageText</tt> - the label of the next/previous page button. These properties are used when the pager <tt>Mode</tt> is <tt>NextPrev</tt> or <tt>Numeric</tt>.</li>
+<li><tt>FirstPageText</tt> and <tt>LastPageText</tt> - the label of the first/last page button. If empty, the corresponding button will not be displayed. These properties are used when the pager <tt>Mode</tt> is <tt>NextPrev</tt> or <tt>Numeric</tt>.</li>
+<li><tt>PageButtonCount</tt> - the maximum number of page index buttons on a page. This property is used when the pager <tt>Mode</tt> is <tt>Numeric</tt>.</li>
+<li><tt>ButtonType</tt> - type of page buttons, either <tt>PushButton</tt> meaning normal form submission buttons, or <tt>LinkButton</tt> meaning hyperlink buttons.</li>
+</ul>
+
+<p>
+<tt>TPager</tt> raises an <tt>OnPageIndexChanged</tt> event when an end-user interacts with it and specifies a new page (e.g. by clicking on a next page button that would lead to the next page.) Developers may write handlers to respond to this event and obtain the desired new page index from the event parameter's property <tt>NewPageIndex</tt>. Using this new page index, one can feed a new page of data to the associated data-bound control.
+</p>
+
+<com:RunBar PagePath="Controls.Samples.TPager.Sample1" />
+
+</com:TContent> \ No newline at end of file
diff --git a/demos/quickstart/protected/pages/Controls/Samples/TPager/Sample1.page b/demos/quickstart/protected/pages/Controls/Samples/TPager/Sample1.page
new file mode 100644
index 00000000..0d61f580
--- /dev/null
+++ b/demos/quickstart/protected/pages/Controls/Samples/TPager/Sample1.page
@@ -0,0 +1,77 @@
+<com:TContent ID="body">
+
+<h1>TPager Sample</h1>
+<p>
+The following sample displays three different pagers associated with a single TDataList control. The datalist control is enabled with custom paging, which allows it to read only one page of data each time. This is typical in DB-driven applications.
+</p>
+
+<div>
+Go to page:
+<com:TPager ID="Pager"
+ ControlToPaginate="DataList"
+ PageButtonCount="3"
+ Mode="Numeric"
+ OnPageIndexChanged="pageChanged"
+ />
+</div>
+
+<com:TDataList
+ ID="DataList"
+ AllowPaging="true"
+ AllowCustomPaging="true"
+ PageSize="2"
+ EnableViewState="true"
+ RepeatColumns="2"
+ RepeatDirection="Vertical"
+ ItemStyle.Font.Italic="true"
+ ItemStyle.BackColor="#BFCFFF"
+ HeaderStyle.BackColor="black"
+ HeaderStyle.ForeColor="white"
+ FooterStyle.BackColor="gray"
+ AlternatingItemStyle.BackColor="#E6ECFF">
+
+<prop:HeaderTemplate>
+<div style="font-weight:bold; text-align:center;">Computer Parts</div>
+</prop:HeaderTemplate>
+
+<prop:FooterTemplate>
+<div style="font-weight:bold; text-align:center;">
+Total <%# $this->Parent->PageCount %> pages.
+</div>
+</prop:FooterTemplate>
+
+<prop:ItemTemplate>
+<table border="0" width="300">
+<tr>
+ <th>ID</th><th>Name</th><th>Quantity</th><th>Price</th>
+</tr>
+<tr>
+ <td align="right"><com:TLiteral Text=<%#$this->DataItem['id'] %> /></td>
+ <td align="right"><com:TLiteral Text=<%#$this->DataItem['name'] %> /></td>
+ <td align="right"><com:TLiteral Text=<%#$this->DataItem['quantity'] %> /></td>
+ <td align="right">$<com:TLiteral Text=<%#$this->DataItem['price'] %> /></td>
+</tr>
+</table>
+</prop:ItemTemplate>
+
+</com:TDataList>
+
+<com:TPager ID="Pager2"
+ ControlToPaginate="DataList"
+ FirstPageText="9"
+ LastPageText=":"
+ Mode="NextPrev"
+ NextPageText="4"
+ PrevPageText="3"
+ Font.Name="Webdings"
+ OnPageIndexChanged="pageChanged"
+ />
+<br/>
+Choose page:
+<com:TPager ID="Pager3"
+ ControlToPaginate="DataList"
+ Mode="List"
+ OnPageIndexChanged="pageChanged"
+ />
+
+</com:TContent> \ No newline at end of file
diff --git a/demos/quickstart/protected/pages/Controls/Samples/TPager/Sample1.php b/demos/quickstart/protected/pages/Controls/Samples/TPager/Sample1.php
new file mode 100644
index 00000000..e5446f60
--- /dev/null
+++ b/demos/quickstart/protected/pages/Controls/Samples/TPager/Sample1.php
@@ -0,0 +1,82 @@
+<?php
+
+class Sample1 extends TPage
+{
+ /**
+ * Returns total number of data items.
+ * In DB-driven applications, this typically requires
+ * execution of an SQL statement with COUNT function.
+ * Here we simply return a constant number.
+ */
+ protected function getDataItemCount()
+ {
+ return 19;
+ }
+
+ /**
+ * Fetches a page of data.
+ * In DB-driven applications, this can be achieved by executing
+ * an SQL query with LIMIT clause.
+ */
+ protected function getData($offset,$limit)
+ {
+ $data=array(
+ array('id'=>'ITN001','name'=>'Motherboard','quantity'=>1,'price'=>100.00,'imported'=>true),
+ array('id'=>'ITN002','name'=>'CPU','quantity'=>1,'price'=>150.00,'imported'=>true),
+ array('id'=>'ITN003','name'=>'Harddrive','quantity'=>2,'price'=>80.00,'imported'=>true),
+ array('id'=>'ITN004','name'=>'Sound card','quantity'=>1,'price'=>40.00,'imported'=>false),
+ array('id'=>'ITN005','name'=>'Video card','quantity'=>1,'price'=>150.00,'imported'=>true),
+ array('id'=>'ITN006','name'=>'Keyboard','quantity'=>1,'price'=>20.00,'imported'=>false),
+ array('id'=>'ITN007','name'=>'Monitor','quantity'=>2,'price'=>300.00,'imported'=>true),
+ array('id'=>'ITN008','name'=>'CDRW drive','quantity'=>1,'price'=>40.00,'imported'=>true),
+ array('id'=>'ITN009','name'=>'Cooling fan','quantity'=>2,'price'=>10.00,'imported'=>false),
+ array('id'=>'ITN010','name'=>'Video camera','quantity'=>20,'price'=>30.00,'imported'=>true),
+ array('id'=>'ITN011','name'=>'Card reader','quantity'=>10,'price'=>24.00,'imported'=>true),
+ array('id'=>'ITN012','name'=>'Floppy drive','quantity'=>50,'price'=>12.00,'imported'=>false),
+ array('id'=>'ITN013','name'=>'CD drive','quantity'=>25,'price'=>20.00,'imported'=>true),
+ array('id'=>'ITN014','name'=>'DVD drive','quantity'=>15,'price'=>80.00,'imported'=>true),
+ array('id'=>'ITN015','name'=>'Mouse pad','quantity'=>50,'price'=>5.00,'imported'=>false),
+ array('id'=>'ITN016','name'=>'Network cable','quantity'=>40,'price'=>8.00,'imported'=>true),
+ array('id'=>'ITN017','name'=>'Case','quantity'=>8,'price'=>65.00,'imported'=>false),
+ array('id'=>'ITN018','name'=>'Surge protector','quantity'=>45,'price'=>15.00,'imported'=>false),
+ array('id'=>'ITN019','name'=>'Speaker','quantity'=>35,'price'=>65.00,'imported'=>false),
+ );
+ return array_slice($data,$offset,$limit);
+ }
+
+ /**
+ * Determines which page of data to be displayed and
+ * populates the datalist with the fetched data.
+ */
+ protected function populateData()
+ {
+ $offset=$this->DataList->CurrentPageIndex*$this->DataList->PageSize;
+ $limit=$this->DataList->PageSize;
+ if($offset+$limit>$this->DataList->VirtualItemCount)
+ $limit=$this->DataList->VirtualItemCount-$offset;
+ $data=$this->getData($offset,$limit);
+ $this->DataList->DataSource=$data;
+ $this->DataList->dataBind();
+ }
+
+ public function onLoad($param)
+ {
+ parent::onLoad($param);
+ if(!$this->IsPostBack)
+ {
+ $this->DataList->VirtualItemCount=$this->DataItemCount;
+ $this->populateData();
+ }
+ }
+
+ /**
+ * Event handler to the OnPageIndexChanged event of pagers.
+ */
+ public function pageChanged($sender,$param)
+ {
+ $this->DataList->CurrentPageIndex=$param->NewPageIndex;
+ $this->populateData();
+ }
+}
+
+?> \ No newline at end of file
diff --git a/demos/quickstart/protected/pages/Controls/Standard.page b/demos/quickstart/protected/pages/Controls/Standard.page
index dfaee35b..156b39e5 100644
--- a/demos/quickstart/protected/pages/Controls/Standard.page
+++ b/demos/quickstart/protected/pages/Controls/Standard.page
@@ -80,6 +80,10 @@
</li>
<li>
+ <a href="?page=Controls.Pager">TPager</a> generates UI that allows users to interactively specify which page of data to be displayed in a data-bound control.
+ </li>
+
+ <li>
<a href="?page=Controls.Panel">TPanel</a> represents a container for other controls on a Web page. In HTML, it is displayed as a &lt;div&gt; element.
</li>
diff --git a/framework/Exceptions/messages.txt b/framework/Exceptions/messages.txt
index 5099e9b3..38541fed 100644
--- a/framework/Exceptions/messages.txt
+++ b/framework/Exceptions/messages.txt
@@ -293,6 +293,7 @@ parametermodule_parameterfile_unchangeable = TParameterModule.ParameterFile is n
parametermodule_parameterfile_invalid = TParameterModule.ParameterFile '{0}' is invalid. Make sure it is in namespace format and the file extension is '.xml'.
parametermodule_parameterid_required = Parameter element must have 'id' attribute.
+datagridcolumn_id_invalid = {0}.ID '{1}' is invalid. Only alphanumeric and underline characters are allowed. The first character must be an alphabetic or underline character.
datagridcolumn_expression_invalid = {0} is evaluating an invalid expression '{1}' : {2}
outputcache_cachemoduleid_invalid = TOutputCache.CacheModuleID is set with an invalid cache module ID {0}. Either the module does not exist or does not implement ICache interface.
@@ -304,6 +305,19 @@ stack_empty = TStack is empty.
queue_data_not_iterable = TQueue can only fetch data from an array or a traversable object.
queue_empty = TQueue is empty.
+pager_pagebuttoncount_invalid = TPager.PageButtonCount must be an integer no less than 1.
+pager_currentpageindex_invalid = TPager.CurrentPageIndex is out of range.
+pager_pagecount_invalid = TPager.PageCount cannot be smaller than 0.
+pager_controltopaginate_invalid = TPager.ControlToPaginate {0} must be a valid ID path pointing to a TDataBoundControl-derived control.
+
+databoundcontrol_pagesize_invalid = {0}.PageSize must be an integer no smaller than 1.
+databoundcontrol_virtualitemcount_invalid = {0}.VirtualItemCount must be an integer no smaller than 0.
+databoundcontrol_currentpageindex_invalid = {0}.CurrentPageIndex is out of range.
+databoundcontrol_datasource_invalid = {0}.DataSource is not valid.
+databoundcontrol_datasourceid_inexistent = databoundcontrol_datasourceid_inexistent.
+databoundcontrol_datasourceid_invalid = databoundcontrol_datasourceid_invalid
+databoundcontrol_datamember_invalid = databoundcontrol_datamember_invalid
+
callback_not_support_no_priority_state_update = Callback request does not support unprioritized pagestate update.
callback_invalid_callback_options = '{1}' is not a valid TCallbackOptions control for Callback control '{0}'.
callback_invalid_clientside_options = Callback ClientSide property must be either a string that is the ID of a TCallbackOptions control or an instance of TCallbackClientSideOptions.=======
@@ -316,7 +330,7 @@ callback_decay_be_not_negative = Decay rate for TCallbackTimer "{0}" must be n
callback_no_autopostback = Control "{0}" can not enable AutoPostBack.
-xmltransform_xslextension_required = TXmlTransform require the PHP's XSL extension
-xmltransform_transformpath_invalid = TransformPath '{0}' is invalid.
-xmltransform_documentpath_invalid = DocumentPath '{0}' is invalid.
-xmltransform_transform_required = Either TransformContent or TransformPath property must be set.
+xmltransform_xslextension_required = TXmlTransform requires the PHP's XSL extension.
+xmltransform_transformpath_invalid = TXmlTransform.TransformPath '{0}' is invalid.
+xmltransform_documentpath_invalid = TXmlTransform.DocumentPath '{0}' is invalid.
+xmltransform_transform_required = Either TransformContent or TransformPath property must be set for TXmlTransform.
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..f811edaf 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()
@@ -734,7 +634,7 @@ class TDataGrid extends TBaseDataList implements INamingContainer
if(strcasecmp($command,self::CMD_SELECT)===0)
{
$this->setSelectedItemIndex($param->getItem()->getItemIndex());
- $this->onSelectedIndexChanged(null);
+ $this->onSelectedIndexChanged($param);
return true;
}
else if(strcasecmp($command,self::CMD_EDIT)===0)
@@ -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('&nbsp;');
- 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('&nbsp;');
+ $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('&nbsp;');
+ $controls->add("\n");
}
if($pageCount>$endPageIndex)
{
- $controls->add('&nbsp;');
- $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/TDataGridColumn.php b/framework/Web/UI/WebControls/TDataGridColumn.php
index e43b4895..8c34b85f 100644
--- a/framework/Web/UI/WebControls/TDataGridColumn.php
+++ b/framework/Web/UI/WebControls/TDataGridColumn.php
@@ -51,10 +51,33 @@ Prado::using('System.Util.TDataFieldAccessor');
*/
abstract class TDataGridColumn extends TApplicationComponent
{
+ private $_id='';
private $_owner=null;
private $_viewState=array();
/**
+ * @return string the ID of the column.
+ */
+ public function getID()
+ {
+ return $this->_id;
+ }
+
+ /**
+ * Sets the ID of the column.
+ * By explicitly specifying the column ID, one can access the column
+ * by $templateControl->ColumnID.
+ * @param string the ID of the column.
+ * @throws TInvalidDataValueException if the ID is of bad format
+ */
+ public function setID($value)
+ {
+ if(!preg_match(TControl::ID_FORMAT,$value))
+ throw new TInvalidDataValueException('datagridcolumn_id_invalid',get_class($this),$value);
+ $this->_id=$value;
+ }
+
+ /**
* @return string the text to be displayed in the header of this column
*/
public function getHeaderText()
diff --git a/framework/Web/UI/WebControls/TDataList.php b/framework/Web/UI/WebControls/TDataList.php
index b6888225..48392b1d 100644
--- a/framework/Web/UI/WebControls/TDataList.php
+++ b/framework/Web/UI/WebControls/TDataList.php
@@ -677,7 +677,7 @@ class TDataList extends TBaseDataList implements INamingContainer, IRepeatInfoUs
if(strcasecmp($command,self::CMD_SELECT)===0)
{
$this->setSelectedItemIndex($param->getItem()->getItemIndex());
- $this->onSelectedIndexChanged(null);
+ $this->onSelectedIndexChanged($param);
return true;
}
else if(strcasecmp($command,self::CMD_EDIT)===0)
diff --git a/framework/Web/UI/WebControls/TMultiView.php b/framework/Web/UI/WebControls/TMultiView.php
index b6001f90..00c0ce93 100644
--- a/framework/Web/UI/WebControls/TMultiView.php
+++ b/framework/Web/UI/WebControls/TMultiView.php
@@ -144,7 +144,7 @@ class TMultiView extends TControl
{
if($view->getActive())
return;
- $triggerEvent=($this->getControlStage()>=TControl::CS_STATE_LOADED || ($this->getPage() && !$this->getPage()->getIsPostBack()));
+ $triggerEvent=$triggerViewChangedEvent && ($this->getControlStage()>=TControl::CS_STATE_LOADED || ($this->getPage() && !$this->getPage()->getIsPostBack()));
foreach($this->getViews() as $v)
{
if($v===$view)
@@ -153,8 +153,7 @@ class TMultiView extends TControl
if($triggerEvent)
{
$view->onActivate(null);
- if($triggerViewChangedEvent)
- $this->onActiveViewChanged(null);
+ $this->onActiveViewChanged(null);
}
}
else if($v->getActive())
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'));
}
diff --git a/tests/FunctionalTests/quickstart/Controls/PagerTestCase.php b/tests/FunctionalTests/quickstart/Controls/PagerTestCase.php
new file mode 100644
index 00000000..2ecb3452
--- /dev/null
+++ b/tests/FunctionalTests/quickstart/Controls/PagerTestCase.php
@@ -0,0 +1,58 @@
+<?php
+
+class PagerTestCase extends SeleniumTestCase
+{
+ function test()
+ {
+ $this->open("../../demos/quickstart/index.php?page=Controls.Samples.TPager.Sample1&amp;notheme=true", "");
+
+ // verify datalist content
+ $this->verifyTextPresent('ITN001','');
+ $this->verifyTextPresent('ITN002','');
+ $this->verifyTextNotPresent('ITN003','');
+
+ // verify numeric paging
+ $this->clickAndWait("ctl0_body_Pager_ctl1", ""); // 2nd page
+ $this->verifyTextPresent('ITN003','');
+ $this->verifyTextPresent('ITN004','');
+ $this->verifyTextNotPresent('ITN002','');
+ $this->verifyTextNotPresent('ITN005','');
+ $this->clickAndWait("ctl0_body_Pager_ctl3", ""); // 4rd page
+ $this->verifyTextPresent('ITN007','');
+ $this->verifyTextPresent('ITN008','');
+ $this->verifyTextNotPresent('ITN006','');
+ $this->verifyTextNotPresent('ITN009','');
+ $this->clickAndWait("ctl0_body_Pager_ctl6", ""); // last page
+ $this->verifyTextPresent('ITN019','');
+ $this->verifyTextNotPresent('ITN018','');
+ $this->verifyTextNotPresent('ITN001','');
+
+ // verify next-prev paging
+ $this->clickAndWait("ctl0_body_Pager2_ctl1", ""); // prev page
+ $this->verifyTextPresent('ITN017','');
+ $this->verifyTextPresent('ITN018','');
+ $this->verifyTextNotPresent('ITN019','');
+ $this->verifyTextNotPresent('ITN016','');
+ $this->clickAndWait("ctl0_body_Pager2_ctl0", ""); // first page
+ $this->verifyTextPresent('ITN001','');
+ $this->verifyTextPresent('ITN002','');
+ $this->verifyTextNotPresent('ITN003','');
+ $this->clickAndWait("ctl0_body_Pager2_ctl2", ""); // next page
+ $this->verifyTextPresent('ITN003','');
+ $this->verifyTextPresent('ITN004','');
+ $this->verifyTextNotPresent('ITN002','');
+ $this->verifyTextNotPresent('ITN005','');
+
+ $this->verifySelected("ctl0_body_Pager3_ctl0","label=2");
+ $this->selectAndWait("ctl0_body_Pager3_ctl0", "label=5");
+ $this->verifyTextPresent('ITN009','');
+ $this->verifyTextPresent('ITN010','');
+ $this->verifyTextNotPresent('ITN008','');
+ $this->verifyTextNotPresent('ITN011','');
+ $this->selectAndWait("ctl0_body_Pager3_ctl0", "label=10");
+ $this->verifyTextPresent('ITN019','');
+ $this->verifyTextNotPresent('ITN018','');
+ }
+}
+
+?> \ No newline at end of file
diff --git a/tests/FunctionalTests/tickets/protected/pages/Ticket239.page b/tests/FunctionalTests/tickets/protected/pages/Ticket239.page
new file mode 100644
index 00000000..a67c38a8
--- /dev/null
+++ b/tests/FunctionalTests/tickets/protected/pages/Ticket239.page
@@ -0,0 +1,19 @@
+<com:TContent ID="Content">
+
+<com:TMultiView ActiveViewIndex="0">
+ <com:TView ID="view1" OnActivate="activateView" OnDeactivate="deactivateView">
+ view1
+ <com:TButton Text="Next view" CommandName="NextView" />
+ </com:TView>
+ <com:TView ID="view2" OnActivate="activateView" OnDeactivate="deactivateView">
+ view2
+ <com:TButton Text="Prev view" CommandName="PreviousView" />
+ <com:TButton Text="Next view" CommandName="NextView" />
+ </com:TView>
+ <com:TView ID="view3" OnActivate="activateView" OnDeactivate="deactivateView">
+ view3
+ <com:TButton Text="Prev view" CommandName="PreviousView" />
+ </com:TView>
+</com:TMultiView>
+<com:TLabel ID="Result" EnableViewState="false" />
+</com:TContent> \ No newline at end of file
diff --git a/tests/FunctionalTests/tickets/protected/pages/Ticket239.php b/tests/FunctionalTests/tickets/protected/pages/Ticket239.php
new file mode 100644
index 00000000..a40c94b5
--- /dev/null
+++ b/tests/FunctionalTests/tickets/protected/pages/Ticket239.php
@@ -0,0 +1,16 @@
+<?php
+
+class Ticket239 extends TPage
+{
+ public function activateView($sender,$param)
+ {
+ $this->Result->Text.=$sender->ID." is activated. ";
+ }
+
+ public function deactivateView($sender,$param)
+ {
+ $this->Result->Text.=$sender->ID." is deactivated. ";
+ }
+}
+
+?> \ No newline at end of file
diff --git a/tests/FunctionalTests/tickets/tests/Ticket239TestCase.php b/tests/FunctionalTests/tickets/tests/Ticket239TestCase.php
new file mode 100644
index 00000000..9d895cb4
--- /dev/null
+++ b/tests/FunctionalTests/tickets/tests/Ticket239TestCase.php
@@ -0,0 +1,55 @@
+<?php
+
+class Ticket239TestCase extends SeleniumTestCase
+{
+ function test()
+ {
+ $this->open('tickets/index.php?page=Ticket239');
+
+ // view1
+ $this->verifyTextPresent('view1 is activated','');
+ $this->verifyTextNotPresent('view1 is deactivated','');
+ $this->verifyTextNotPresent('view2 is activated','');
+ $this->verifyTextNotPresent('view2 is deactivated','');
+ $this->verifyTextNotPresent('view3 is activated','');
+ $this->verifyTextNotPresent('view3 is deactivated','');
+
+ // goto view2
+ $this->clickAndWait('name=ctl0$Content$ctl1');
+ $this->verifyTextNotPresent('view1 is activated','');
+ $this->verifyTextPresent('view1 is deactivated','');
+ $this->verifyTextPresent('view2 is activated','');
+ $this->verifyTextNotPresent('view2 is deactivated','');
+ $this->verifyTextNotPresent('view3 is activated','');
+ $this->verifyTextNotPresent('view3 is deactivated','');
+
+ // goto view3
+ $this->clickAndWait('name=ctl0$Content$ctl3');
+ $this->verifyTextNotPresent('view1 is activated','');
+ $this->verifyTextNotPresent('view1 is deactivated','');
+ $this->verifyTextNotPresent('view2 is activated','');
+ $this->verifyTextPresent('view2 is deactivated','');
+ $this->verifyTextPresent('view3 is activated','');
+ $this->verifyTextNotPresent('view3 is deactivated','');
+
+ // goto view2
+ $this->clickAndWait('name=ctl0$Content$ctl4');
+ $this->verifyTextNotPresent('view1 is activated','');
+ $this->verifyTextNotPresent('view1 is deactivated','');
+ $this->verifyTextPresent('view2 is activated','');
+ $this->verifyTextNotPresent('view2 is deactivated','');
+ $this->verifyTextNotPresent('view3 is activated','');
+ $this->verifyTextPresent('view3 is deactivated','');
+
+ // goto view1
+ $this->clickAndWait('name=ctl0$Content$ctl2');
+ $this->verifyTextPresent('view1 is activated','');
+ $this->verifyTextNotPresent('view1 is deactivated','');
+ $this->verifyTextNotPresent('view2 is activated','');
+ $this->verifyTextPresent('view2 is deactivated','');
+ $this->verifyTextNotPresent('view3 is activated','');
+ $this->verifyTextNotPresent('view3 is deactivated','');
+ }
+}
+
+?> \ No newline at end of file