diff options
Diffstat (limited to 'framework/Web')
-rw-r--r-- | framework/Web/UI/WebControls/TDataBoundControl.php | 192 | ||||
-rw-r--r-- | framework/Web/UI/WebControls/TListBox.php | 58 | ||||
-rw-r--r-- | framework/Web/UI/WebControls/TListControl.php | 63 | ||||
-rw-r--r-- | framework/Web/UI/WebControls/TWebControl.php | 2 |
4 files changed, 234 insertions, 81 deletions
diff --git a/framework/Web/UI/WebControls/TDataBoundControl.php b/framework/Web/UI/WebControls/TDataBoundControl.php index 032370f7..8f447607 100644 --- a/framework/Web/UI/WebControls/TDataBoundControl.php +++ b/framework/Web/UI/WebControls/TDataBoundControl.php @@ -6,8 +6,11 @@ abstract class TDataBoundControl extends TWebControl private $_dataSource=null;
private $_requiresBindToNull=false;
private $_requiresDataBinding=false;
- private $_throwOnDataPropertyChange=false;
private $_prerendered=false;
+ private $_currentView=null;
+ private $_currentDataSource=null;
+ private $_currentViewValid=false;
+ private $_currentDataSourceValid=false;
/**
* @return Traversable data source object, defaults to null.
@@ -18,14 +21,16 @@ abstract class TDataBoundControl extends TWebControl }
/**
+ * Sets the data source object associated with the databound control.
+ * The data source must implement Traversable interface.
+ * If an array is given, it will be converted to xxx.
+ * If a string is given, it will be converted to xxx.
* @param Traversable|array|string data source object
*/
public function setDataSource($value)
{
- if($value!==null)
- $this->validateDataSource($value);
- $this->_dataSource=$value;
- $this->onDataPropertyChanged();
+ $this->_dataSource=$this->validateDataSource($value);;
+ $this->onDataSourceChanged();
}
/**
@@ -46,22 +51,34 @@ abstract class TDataBoundControl extends TWebControl if($dsid!=='' && $value==='')
$this->_requiresBindToNull=true;
$this->setViewState('DataSourceID',$value,'');
- $this->onDataPropertyChanged();
+ $this->onDataSourceChanged();
}
/**
+ * @return boolean if the databound control uses the data source specified
+ * by {@link setDataSourceID}, or it uses the data source object specified
+ * by {@link setDataSource}.
+ */
+ protected function getUsingDataSourceID()
+ {
+ return $this->getDataSourceID()!=='';
+ }
+
+ /**
+ * Sets {@link setRequiresDataBinding RequiresDataBinding} as true if the control is initialized.
* This method is invoked when either {@link setDataSource} or {@link setDataSourceID} is changed.
*/
- protected function onDataPropertyChanged()
+ protected function onDataSourceChanged()
{
- if($this->_throwOnDataPropertyChanged)
- throw new TInvalidOperationException('databoundcontrol_dataproperty_unchangeable');
+ $this->_currentViewValid=false;
+ $this->_currentDataSourceValid=false;
if($this->getInitialized())
$this->setRequiresDataBinding(true);
}
/**
* @return boolean whether the databound control has been initialized.
+ * By default, the control is initialized after its viewstate has been restored.
*/
protected function getInitialized()
{
@@ -69,6 +86,10 @@ abstract class TDataBoundControl extends TWebControl }
/**
+ * Sets a value indicating whether the databound control is initialized.
+ * If initialized, any modification to {@link setDataSource DataSource} or
+ * {@link setDataSourceID DataSourceID} will set {@link setRequiresDataBinding RequiresDataBinding}
+ * as true.
* @param boolean a value indicating whether the databound control is initialized.
*/
protected function setInitialized($value)
@@ -77,13 +98,19 @@ abstract class TDataBoundControl extends TWebControl }
/**
- * @return boolean if the databound control uses the data source control specified
- * by {@link setDataSourceID}, or it uses the data source object specified
- * by {@link setDataSource}.
+ * @return boolean if databind has been invoked in the previous page request
*/
- protected function getUsingDataSourceID()
+ protected function getIsDataBound()
{
- return $this->getDataSourceID()!=='';
+ return $this->getViewState('IsDataBound',false);
+ }
+
+ /**
+ * @param boolean if databind has been invoked in this page request
+ */
+ protected function setIsDataBound($value)
+ {
+ $this->setViewState('IsDataBound',TPropertyValue::ensureBoolean($value),false);
}
/**
@@ -103,7 +130,7 @@ abstract class TDataBoundControl extends TWebControl protected function setRequiresDataBinding($value)
{
$value=TPropertyValue::ensureBoolean($value);
- if($value && $this->_prerendered && $this->getUsingDataSourceID())
+ if($value && $this->_prerendered)
{
$this->_requiresDataBinding=true;
$this->ensureDataBound();
@@ -113,41 +140,88 @@ abstract class TDataBoundControl extends TWebControl }
/**
+ * Ensures any pending {@link dataBind} is called.
+ * This method calls {@link dataBind} if the data source is specified
+ * by {@link setDataSourceID} or if {@link getRequiresDataBinding RequiresDataBinding}
+ * is true.
+ */
+ protected function ensureDataBound()
+ {
+ if($this->_requiresDataBinding && ($this->getUsingDataSourceID() || $this->_requiresBindToNull))
+ {
+ $this->dataBind();
+ $this->_requiresBindToNull=false;
+ }
+ }
+
+ /**
* Performs databinding.
* This method overrides the parent implementation by calling
* {@link performSelect} which fetches data from data source and does
* the actual binding work.
- * @param boolean whether to raise DataBind event. This parameter is ignored.
*/
- public function dataBind($raiseDataBindingEvent=true)
+ public function dataBind()
{
- $this->performSelect();
+ // TODO: databinding should only be raised after data is ready
+ // what about property bindings? should they be after data is ready?
+ $this->setRequiresDataBinding(false);
+ $this->dataBindProperties();
+ $view=$this->getDataSourceView();
+ $data=$view->select($this->getSelectParameters());
+ $this->onDataBinding(null);
+ $this->performDataBinding($data);
+ $this->setIsDataBound(true);
+ $this->onDataBound(null);
}
- /**
- * Ensures any pending {@link dataBind} is called.
- * This method calls {@link dataBind} if the data source is specified
- * by {@link setDataSourceID} or if {@link getRequiresDataBinding RequiresDataBinding}
- * is true.
- */
- protected function ensureDataBound()
+ public function dataSourceViewChanged($sender,$param)
+ {
+ if(!$this->_ignoreDataSourceViewChanged)
+ $this->setRequiresDataBinding(true);
+ }
+
+ protected function getDataSourceView()
{
- try
+ if(!$this->_currentViewValid)
{
- $this->_throwOnDataPropertyChange=true;
- if($this->_requiresDataBinding && ($this->getUsingDataSourceID() || $this->_requiresBindToNull))
- {
- $this->dataBind();
- $this->_requiresBindToNull=false;
- }
+ if($this->_currentView && $this->_currentViewIsFromDataSourceID)
+ $handlers=$this->_currentView->detachEventHandler('DataSourceViewChanged',array($this,'dataSourceViewChanged'));
+ $dataSource=$this->determineDataSource();
+ if(($view=$dataSource->getView($this->getDataMember()))===null)
+ throw new TInvalidDataValueException('databoundcontrol_datamember_invalid',$this->getDataMember());
+ if($this->_currentViewIsFromDataSourceID=$this->getUsingDataSourceID())
+ $view->attachEventHandler('DataSourceViewChanged',array($this,'dataSourceViewChanged'));
+ $this->_currentView=$view;
+ $this->_currentViewValid=true;
}
- catch(Exception $e)
+ return $this->_currentView;
+ }
+
+ protected function determineDataSource()
+ {
+ if(!$this->_currentDataSourceValid)
{
- $this->_throwOnDataPropertyChange=false;
- throw $e;
+ $dsid=$this->getDataSourceID();
+ if($dsid!=='')
+ {
+ if(($dataSource=$this->getNamingContainer()->findControl($dsid))===null)
+ throw new TInvalidDataValueException('databoundcontrol_datasourceid_inexistent',$dsid);
+ else if(!($dataSource instanceof IDataSource))
+ throw new TInvalidDataValueException('databoundcontrol_datasourceid_invalid',$dsid);
+ else
+ $this->_currentDataSource=$dataSource;
+ }
+ else
+ {
+ $this->_currentDataSource=new TReadOnlyDataSource($this->getDataSource(),$this->getDataMember());
+ }
+ $this->_currentDataSourceValid=true;
}
+ return $this->_currentDataSource;
}
+ abstract protected function performDataBinding($data);
+
/**
* Raises <b>DataBound</b> event.
* This method should be invoked after a databind is performed.
@@ -170,8 +244,6 @@ abstract class TDataBoundControl extends TWebControl parent::onInit($param);
$page=$this->getPage();
$page->attachEventHandler('PreLoad',array($this,'onPagePreLoad'));
- if(!$this->getEnableViewState(true) && $page->getIsPostBack())
- $this->setRequiresDataBinding(true);
}
/**
@@ -183,6 +255,9 @@ abstract class TDataBoundControl extends TWebControl protected function onPagePreLoad($sender,$param)
{
$this->_initialized=true;
+ $isPostBack=$this->getPage()->getIsPostBack();
+ if(!$isPostBack || ($isPostBack && (!$this->getEnableViewState(true) || !$this->getIsDataBound())))
+ $this->setRequiresDataBinding(true);
}
/**
@@ -199,39 +274,17 @@ abstract class TDataBoundControl extends TWebControl /**
* Validates if the parameter is a valid data source.
- * @return boolean if the parameter is a valid data source
+ * If it is a string or an array, it will be converted as a TList object.
+ * @return Traversable the data that is traversable
+ * @throws TInvalidDataTypeException if the data is neither null nor Traversable
*/
protected function validateDataSource($value)
{
- if(!is_array($value) && !($value instanceof Traversable))
+ if(is_array($value))
+ $value=new TList($value);
+ else if($value!==null && !($value instanceof Traversable))
throw new TInvalidDataTypeException('databoundcontrol_datasource_invalid');
- }
-
- /**
- * @return ???
- */
- protected function performSelect()
- {
- if(!$this->getUsingDataSourceID())
- $this->onDataBinding(null);
- $view=$this->getDataSourceView();
- $this->setRequiresDataBinding(false);
- $this->setDataBound(true);
- $data=$view->select($this->getSelectParameters());
- if($this->getUsingDataSourceID())
- $this->onDataBinding(null);
- $this->performDataBinding($data);
- $this->onDataBound(null);
- }
-
- protected function getDataSourceView()
- {
- $source=$this->getDataSourceByID();
- return $source->getView($this->getDataMember());
- }
-
- protected function performDataBinding($data)
- {
+ return $value;
}
public function getDataMember()
@@ -247,14 +300,9 @@ abstract class TDataBoundControl extends TWebControl public function getSelectParameters()
{
if(!$this->_parameters)
- $this->_parameters=$this->createSelectParameters();
+ $this->_parameters=new TDataSourceSelectParameters;
return $this->_parameters;
}
-
- protected function createSelectParameters()
- {
- return new TDataSourceSelectParameters;
- }
}
?>
\ No newline at end of file diff --git a/framework/Web/UI/WebControls/TListBox.php b/framework/Web/UI/WebControls/TListBox.php index 79afc133..4aabbbac 100644 --- a/framework/Web/UI/WebControls/TListBox.php +++ b/framework/Web/UI/WebControls/TListBox.php @@ -6,14 +6,17 @@ class TListBox extends TListControl implements IPostBackDataHandler {
$rows=$this->getRows();
$writer->addAttribute('size',"$rows");
- $writer->addAttribute('name',$this->getUniqueID());
+ if($this->getSelectionMode()==='Multiple')
+ $writer->addAttribute('name',$this->getUniqueID().'[]');
+ else
+ $writer->addAttribute('name',$this->getUniqueID());
parent::addAttributesToRender($writer);
}
protected function onPreRender($param)
{
parent::onPreRender($param);
- if($this->getSelectionMode()==='Multiple' && $this->getEnabled(true))
+ if($this->getEnabled(true))
$this->getPage()->registerRequiresPostData($this);
}
@@ -21,7 +24,56 @@ class TListBox extends TListControl implements IPostBackDataHandler {
if(!$this->getEnabled(true))
return false;
- // ensure DataBound???
+ $selections=isset($values[$key])?$values[$key]:null;
+ $this->ensureDataBound();
+ if($selections!==null)
+ {
+ $items=$this->getItems();
+ if($this->getSelectionMode()==='Single')
+ {
+ $selection=is_array($selections)?$selections[0]:$selections;
+ $index=$items->findIndexByValue($selection,false);
+ if($this->getSelectedIndex()!==$index)
+ {
+ $this->setSelectedIndex($index);
+ return true;
+ }
+ else
+ return false;
+ }
+ if(!is_array($selections))
+ $selections=array($selections);
+ $list=array();
+ foreach($selections as $selection)
+ $list[]=$items->findIndexByValue($selection,false);
+ $list2=$this->getSelectedIndices();
+ $n=count($list);
+ $flag=false;
+ if($n===count($list2))
+ {
+ sort($list,SORT_NUMERIC);
+ for($i=0;$i<$n;++$i)
+ {
+ if($list[$i]!==$list2[$i])
+ {
+ $flag=true;
+ break;
+ }
+ }
+ }
+ else
+ $flag=true;
+ if($flag)
+ $this->setSelectedIndices($list);
+ return $flag;
+ }
+ else if($this->getSelectedIndex()!==-1)
+ {
+ $this->clearSelection();
+ return true;
+ }
+ else
+ return false;
}
public function raisePostDataChangedEvent()
diff --git a/framework/Web/UI/WebControls/TListControl.php b/framework/Web/UI/WebControls/TListControl.php index c965ddb0..7e81fd6c 100644 --- a/framework/Web/UI/WebControls/TListControl.php +++ b/framework/Web/UI/WebControls/TListControl.php @@ -54,9 +54,47 @@ abstract class TListControl extends TDataBoundControl $this->getItems()->add($object);
}
+ protected function validateDataSource($value)
+ {
+ if(is_string($value))
+ {
+ $list=new TList;
+ foreach(TPropertyValue::ensureArray($value) as $key=>$value)
+ $list->add(array($value,is_string($key)?$key:$value));
+ return $list;
+ }
+ else
+ return parent::validateDataSource($value);
+ return $value;
+ }
+
protected function performDataBinding($data)
{
- // TODO;
+ if($data instanceof Traversable)
+ {
+ $textField=$this->getDataTextField();
+ if($textField==='')
+ $textField=0;
+ $valueField=$this->getDataValueField();
+ if($valueField==='')
+ $valueField=1;
+ $textFormat=$this->getDataTextFormatString();
+ $items=$this->getItems();
+ if(!$this->getAppendDataBoundItems())
+ $items->clear();
+ foreach($data as $object)
+ {
+ $item=new TListItem;
+ if(isset($object[$textField]))
+ $text=$object[$textField];
+ else
+ $text=TPropertyValue::ensureString($object);
+ $item->setText($textFormat===''?$text:sprintf($textFormat,$text));
+ if(isset($object[$valueField]))
+ $item->setValue($object[$valueField]);
+ $items->add($item);
+ }
+ }
}
protected function onSaveState($param)
@@ -204,7 +242,7 @@ abstract class TListControl extends TDataBoundControl $index=TPropertyValue::ensureInteger($index);
if($this->_items)
{
- $this->_items->clearSelection();
+ $this->clearSelection();
if($index>=0 && $index<$this->_items->getCount())
$this->_items->itemAt($index)->setSelected(true);
}
@@ -223,6 +261,20 @@ abstract class TListControl extends TDataBoundControl return $selections;
}
+ protected function setSelectedIndices($indices)
+ {
+ if($this->_items)
+ {
+ $this->clearSelection();
+ $n=$this->_items->getCount();
+ foreach($indices as $index)
+ {
+ if($index>=0 && $index<$n)
+ $this->_items->itemAt($index)->setSelected(true);
+ }
+ }
+ }
+
/**
* @return TListItem|null the selected item with the lowest cardinal index, null if no selection.
*/
@@ -308,6 +360,7 @@ abstract class TListControl extends TDataBoundControl $this->raiseEvent('SelectedIndexChanged',$this,$param);
}
+ // ????
public function onTextChanged($param)
{
$this->raiseEvent('TextChanged',$this,$param);
@@ -351,12 +404,12 @@ class TListItemCollection extends TList throw new TInvalidDataTypeException('listitemcollection_item_invalid');
}
- public function addAt($index,$item)
+ public function insert($index,$item)
{
if(is_string($item))
- parent::addAt($index,new TListItem($item));
+ parent::insert($index,new TListItem($item));
else if($item instanceof TListItem)
- parent::addAt($index,$item);
+ parent::insert($index,$item);
else
throw new TInvalidDataTypeException('listitemcollection_item_invalid');
}
diff --git a/framework/Web/UI/WebControls/TWebControl.php b/framework/Web/UI/WebControls/TWebControl.php index 33f35c72..4f20b227 100644 --- a/framework/Web/UI/WebControls/TWebControl.php +++ b/framework/Web/UI/WebControls/TWebControl.php @@ -28,7 +28,7 @@ Prado::using('System.Web.UI.WebControls.TStyle'); * such as {@link getBackColor BackColor}, {@link getBorderWidth BorderWidth}, etc.
*
* Subclasses of TWebControl typically needs to override {@link addAttributesToRender}
- * and {@link renderContent}. The former is used to render the attributes
+ * and {@link renderContents}. The former is used to render the attributes
* of the HTML tag associated with the control, while the latter is to render
* the body contents enclosed within the HTML tag.
*
|