sample2

TRepeater can be used in more complex situations. As an example, we show in the following how to use nested repeaters, i.e., repeater in repeater. This is commonly seen in presenting master-detail data.
TRepeaterは、もっと複雑な状況においても使用可能です。次の例として、入れ子になっている(ネスティングされた)リピーターコントロール、すなわちリピーターの中のリピーター の使用方法を示します。 これは、一般的に、主従関係のあるデータを示すことに使われます。
To use a repeater within another repeater, for an item for the outer repeater is created, we need to set the detail data source for the inner repeater. This can be achieved by responding to the OnItemDataBound event of the outer repeater.
リピーターの中のリピーターを使うためには、外のリピーターのアイテムを生成するときに、内側のリピーターに詳細データを設定する必要があります。 これは、外のリピーターのOnItemDataBoundイベントに反応することによってうまくいきます。
An OnItemDataBound event is raised each time an outer repeater item completes databinding. In the following example, we exploit another event of repeater called OnItemCreated, which is raised each time a repeater item (and its content) is newly created.
OnItemDataBoundイベントは、外のリピーターアイテムがdatabindを完了するたびに発生します。以下の例においては、もう一つ別の、OnItemCreatedと呼ばれるリピーターのイベントを利用します(それは、リピーターアイテム(そしてその内容)が新しく作成されるたびに発生します)。
We respond to this event by setting different background colors for repeater items to achieve alternating item background display. This saves us from writing an AlternatingItemTemplate for the repeaters.
交互に起こるアイテムの背景表示をするために、異なった背景色をリピーターアイテムに設定し、アイテムへ反映させます。 これによってAlternatingItemTemplateをリピーターに書くことをしなくて良くなります。

TRY & ERROR

Sample2は、結構複雑な構造をしていて、ここにある説明で「分かれ」というのは、かなり無理があると思います。私は、以下の様に、外側のコントロールから、作って表示させていきました。まずは、pageファイルです。
<table cellspacing="1" style="border:1px solid silver">
 <com:TRepeater ID="Repeater">
  <prop:ItemTemplate>
   <tr>
    <com:TTableCell ID="Cell">
     <%#$this->Data %>
    </com:TTableCell>
     <td>
     </td>
   </tr>
  </prop:ItemTemplate>
 </com:TRepeater>
</table>
Sample1を理解いただけるなら、この部分はお分かりになると思います。テーブル内で、TTableCell というコントロール をTRepeater で繰り返している構造ですね。ちなみにTTableCellは、タグ上では、<td>タグを作るものの様です。中のデータは、Data プロパティとなっていますから、クラスファイルでは、この部分へデータを差込ます。
protected function getMasterData()
{
 return array('North','West','East','South');
}

public function onLoad($param)
{
 parent::onLoad($param);
 if(!$this->IsPostBack)
 {
  $this->Repeater->DataSource=$this->getMasterData();
  $this->Repeater->dataBind();
 }
}
下のメソッド、onLoad によって、読み込み時に getMasterDataメソッドが発動され、このげtMasterDataメソッドによって返された配列データが、Repeater(ページのIDによって指定されたTRepeater コントロールを指します。)へデータバインドされることとなります。
実行すると、外側の表、North,West・・・・と4行出来上がっていますね。
次に、主従関係の従の部分になっていくわけですが、まずは、タグから見えてくる構造を抑えておきたいと思います。外側のTRepeater には、繰り返しの発生するアイテムとして、ItemTemplate があるわけで、その中に、TTableCellを配置するという形になっています。そのすぐ次に、また<td>・・・・</td>とあり、その中に、再度TRepeater コントロールが、配置されていて、どうやら、table を内部に作っているようです。この方法によって、主従関係のあるテーブルが出来上がるというわけですね。これだと、従の行がそのデータ数によって、何行になっても、その行分だけ繰り返して表示してくれるということになります。(データ数によって、主の方の、rowspanを伸ばしているわけではないということですね。)あとは、その内部へのデータの挟み込み方法となるわけなので、この構造の工夫の方が大事なのではと感じました。
その、データの挟み込みですが、外側のTRepeater には、 OnDataBound と OnItemCreated というハンドラが設定されており、内側のTRepeater には、OnItemCreated のみが設定されています。その名前からすると、OnDataBound は、データの挟み込みが行われたとき、OnItemCreated は、Item が作られるたびに という様に想像できるので、前者は、読み込み時の getMasterData()メソッドが生じたときに発生し、後者はItemTemplate が繰り返し生成されるときに発生すると予想できます。念のため、APIマニュアルを翻訳してみました。
OnDataBound
This method is invoked right after an item is data bound. The repeater item control responsible for the event can be determined from the event parameter. If you override this method, be sure to call parent's implementation so that event handlers have chance to respond to the event.
OnDataBound
このメソッドは、アイテムにデータの挟み込みが行われた直後に呼び出されます。このイベントに引き起こされたリピーターアイテムコントロールは、このイベントパラメータによって、決定されることとなります。もし、このメソッドを無効にした場合、親のインプレメントを呼び出し、イベントに反応する機会をイベントハンドラに設定することが必要となってきます。
OnItemCreated
This method is invoked after a repeater item is created and instantiated with template, but before added to the page hierarchy. The repeater item control responsible for the event can be determined from the event parameter. If you override this method, be sure to call parent's implementation so that event handlers have chance to respond to the event.
OnItemCreated
リピーターアイテムが作成され、テンプレートに従ってインスタンス化された後、かつ、ページ階層へ追加される前に、このメソッドは呼び出されます。リピーターアイテムコントロールは、イベントパラメータによって影響されます。 もし、このメソッドを無効にした場合、親のインプレメントを呼び出し、イベントに反応する機会をイベントハンドラに設定することが必要となってきます。
うむむ・・・・ 「データが挟み込みされる時」と「リピーターアイテムが作られる時」の具体的な違いが良く分かりませんね。でも少なくとも、onDataBoundは、データの挟み込みが無いときは発動しないと思われます。イメージとしては、
  1. Repeaterアイテムがテンプレートに従って作られる。(onItemCreated が発動する。)
  2. onLoadなどによって、データの挟み込みが行われる時は、データが挟み込みされる。(onDataBoundが発動する。) 
  3. ページ階層へ追加され表示される。
といった具合でしょうか?(従前の説明からもこのような手続きと思われます。)
ちょっと実験・・・まず、pageファイルの TRepeater コントロールを以下の様に書き換えます。
<com:TRepeater ID="Repeater"
 EnableViewState="false"
 OnItemDataBound="dataBindRepeater2">
次に、pageファイルの末尾に
<com:TLabel ID='test' text='test'>
を追加します。
クラスファイルには、
public function dataBindRepeater2($sender,$param)
{
 $item=$param->Item;
 $this->test->Text=$this->test->Text."_".$item->ItemIndex;

}
を追加します。本来は、このfunctionには、従たるテーブルへのデータ挟み込みが書かれているのですが、分かりやすくするために、先程テーブルの下に付け足したtestラベルにインデックスを連ねたテキストを差し込みます。
この状態で、ページを表示してみると・・・・
test_0_1_2_3
と表示されます。TRepeaterにデータが挟み込みされるたびに起きていることになるのでしょう。もう一丁、onItemCreated のハンドラの方を試してみても同様の結果となります。onItemCreated の方がonDataBound よりも早い段階で作られるというのは上記解説に書いてあるとおりです。でもその違いよりは、データの挟み込みがあるのか、そのデータの挟み込みに影響されるメソッドなのかによって使い分けるのだと思います。
ここまで確認したところで、onItemCreated の方はサンプルどおりに、

 onItemCreated=repeaterItemCreated

へ変更します。このクラスファイルは、
   public function repeaterItemCreated($sender,$param)
   {
       static $itemIndex=0;
       $item=$param->Item;
       if($item->ItemType==='Item' || $item->ItemType==='AlternatingItem')
       {
           $item->Cell->BackColor=$itemIndex%2 ? "#6078BF" : "#809FFF";
           $item->Cell->ForeColor='white';
           $itemIndex++;
       }
   }
となっています。下線部分は三項条件式ですね。私はあまり使わないので、理解するまで時間がかかってしまいました。しかも、条件式には2で割ったあまりを使用していて、偶数なら0(=False)となるようにしています。かっこいいですね。これを使えば、sample1の<prop:AlternatingItemTemplate>記述をすることなく、行の色を変えられるというわけですね。
ついでに、もう一つあるハンドラをちょっとだけチェックしてみました。
onItemCommand
This method is invoked after a button control in a template raises OnCommand event. The repeater control responsible for the event can be determined from the event parameter. The event parameter also contains the information about the initial sender of the OnCommand event, command name and command parameter. You may override this method to provide customized event handling. Be sure to call parent's implementation so that event handlers have chance to respond to the event.
このメソッドは、テンプレートのボタンの制御装置がOnCommandイベントを上げた後に呼び出されます。 イベントに原因となるリピータコントロールはイベントパラメタから決定することができます。 また、イベントパラメタはOnCommandイベント、コマンド名、およびコマンドパラメタの初期状態のsender変数に関する情報を含んでいます。 このメソッドを、カスタマイズしたのもへ変更することができますが、必ず、親インプレメントを呼び出し、イベントハンドラにイベントに反応できる様にしてください。
このonItemCommandは分かりやすいのですが、実際に、Template 内にボタンを作っても、うまくボタンのonCommand が作動しなくて、だめでした。エラーにはならないので、ちょっとしたやり方の違いなのだと思います。
sample2に戻って、次に従たるテーブルへのデータ挟み込み部分を追加すれば、完了です。サンプルは07/10時点でこのままコピーしてもいろいろとエラーが出るので以下に私の書いたものをそのまま載せます。
  • TRepeaterSample2.page
    <h1>TRepeater Sample 2</h1>
     <table cellspacing="1" style="border:1px solid silver">
     <com:TRepeater ID="Repeater"
           EnableViewState="false"
           OnItemDataBound="dataBindRepeater2"
           OnItemCreated="repeaterItemCreated">
     <prop:ItemTemplate>
      <tr>
       <com:TTableCell ID="Cell">
        <%#$this->Data %>
        </com:TTableCell>
    
         <td>
          <com:TRepeater
               ID="Repeater2"
               EnableViewState="false"
               OnItemCreated="Page.repeater2ItemCreated">
     <prop:HeaderTemplate>
       <table cellspacing="1">
     </prop:HeaderTemplate>
     <prop:ItemTemplate>
      <com:TTableRow ID="Row">
       <com:TTableCell Width="70px">
        <%#$this->Data['name'] %>
       </com:TTableCell>
       <com:TTableCell Width="20">
        <%#$this->Data['age'] %>
       </com:TTableCell>
       <com:TTableCell Width="150px">
        <%#$this->Data['position'] %>
       </com:TTableCell>
      </com:TTableRow>
     </prop:ItemTemplate>
    
     <prop:FooterTemplate>
      </table>
     <prop:FooterTemplate>
      </com:TRepeater>
       </td>
       </tr>
     </prop:ItemTemplate>
    
     </com:TRepeater>
    </table>
  • TRepeaterSample2.php
    <?php
    class TRepeaterSample2 extends TPage
    {
     protected function getMasterData()
     {
      return array('North','West','East','South');
     }
    
     public function onLoad($param)
     {
      parent::onLoad($param);
      if(!$this->IsPostBack)
       {
        $this->Repeater->DataSource=$this->getMasterData();
        $this->Repeater->dataBind();
       }
      }
    
     protected function getDetailData($region)
     {
      static $data=array(
       'North'=>array(
                array('name'=>'John','age'=>30,'position'=>'Program Manager'),
                array('name'=>'Edward','age'=>35,'position'=>'Developer'),
                array('name'=>'Walter','age'=>28,'position'=>'Developer'),
        ),
       'West'=>array(
               array('name'=>'Cary','age'=>31,'position'=>'Senior Manager'),
               array('name'=>'Ted','age'=>25,'position'=>'Developer'),
               array('name'=>'Kevin','age'=>28,'position'=>'Developer'),
        ),
       'East'=>array(
               array('name'=>'Shawn','age'=>30,'position'=>'Sales Manager'),
               array('name'=>'Larry','age'=>28,'position'=>'Document Writer'),
        ),
        'South'=>array(
               array('name'=>'King','age'=>30,'position'=>'Program Manager'),
               array('name'=>'Carter','age'=>22,'position'=>'Developer'),
        ),
       );
    
      return $data[$region];
    }
    
     public function dataBindRepeater2($sender,$param)
     {
      $item=$param->Item;
      if($item->ItemType==='Item' || $item->ItemType==='AlternatingItem')
      {
        $item->Repeater2->DataSource=$this->getDetailData($item->DataItem);
        $item->Repeater2->dataBind();
      }
     }
     public function repeaterItemCreated($sender,$param)
     {
      static $itemIndex=0;
      $item=$param->Item;
      if($item->ItemType==='Item' || $item->ItemType==='AlternatingItem')
       {
         $item->Cell->BackColor=$itemIndex%2 ? "#6078BF" : "#809FFF";
         $item->Cell->ForeColor='white';
         $itemIndex++;
       }
     }
    
     public function repeater2ItemCreated($sender,$param)
     {
      static $itemIndex=0;
      $item=$param->Item;
      if($item->ItemType==='Item' || $item->ItemType==='AlternatingItem')
      {
       $item->Row->BackColor=$itemIndex%2 ? "#BFCFFF" : "#E6ECFF";
       $itemIndex++;
      }
     }
    
    }
    ?>
最後にこのsample2の流れをもう一度まとめてみました。
  1. ページが読み込まれ、TRepeater が ItemTemplete を繰り返す。
  2. 繰り返されると、table 内部がつくられ、そのとき onItemCreated イベントハンドらのパラメータであるrepeaterItemCreated関数が呼び出される。
  3. repeaterItemCreated 関数は、テーブル行に対し、奇数行と偶数行で背景色を変えて設定しに行く
  4. 同時にクラスに設定されている onLoad 関数によって getMasterData 関数が呼び出され、テーブルに 親データ North,South,・・・が挟み込まれる。
  5. TRepeater にデータが挟み込まれることで onDataBound ハンドラのdataBindRepeater2関数が呼び出される。
  6. dataBindRepeater2関数は、従テーブルである TRepeater2 へのデータ挟み込みを行う。
  7. dataBindRepeater2関数は、各々getDetailData関数を呼び出し、name,age,position へ詳細データを挟み込む。

このサンプルの結果は、ここでご覧になれます。