RepeaterにRepeaterをネストさせる(クライアント側で実現)
RepeaterにRepeaterをネストさせて商品区分別商品リストをタブコントロール形式で表示します。
●商品区分別商品リストをタブコントロール形式で表示する (ch67TabbedControl1.aspx)
Repeater1にRepeater2をネストさせて商品区分別の商品を表示します。Repeater1にCategories表を表示するには、パッケージ(CategoryPackage)に登録されているストアドプロシージャ(GetCategories)を使用します。このストアドプロシージャは、Categories表からすべての行(レコード)を抽出します。
Repeater2にProducts表を表示するには、パッケージ(ProductPackage)に登録されているストアドプロシージャ(GetProductsByCategoryID)を使用します。このストアドプロシージャは、パラメータに指定した商品区分IDに該当する商品を抽出します。
iSQL*PlusまたはSQL*Plusを起動して、事前にパッケージ仕様部とパッケージ本体部を作成してください。
パッケージ仕様部
CategoryPackage.sql
CREATE OR REPLACE PACKAGE
TYPE rcurCategories IS REF CURSOR;
PROCEDURE GetCategories(
orcurCategories OUT rcurCategories);
END CategoryPackage;
ProductPackage.sql
CREATE OR REPLACE PACKAGE
TYPE rcurProducts IS REF CURSOR;
PROCEDURE GetProductsByCategoryID(
orcurProducts OUT rcurProducts,
iCategoryID IN NUMBER);
END ProductPackage;
パッケージ本体部
CategoryPackage.sql
CREATE OR REPLACE PACKAGE BODY
PROCEDURE GetCategories(
orcurCategories OUT rcurCategories) IS
BEGIN
OPEN orcurCategories FOR
SELECT * FROM Categories
ORDER BY CategoryID;
END GetCategories;
END CategoryPackage;
ProductPackage.sql
CREATE OR REPLACE PACKAGE BODY
PROCEDURE GetProductsByCategoryID(
orcurProducts OUT rcurProducts,
iCategoryID IN NUMBER) IS
BEGIN
OPEN orcurProducts FOR
SELECT *
FROM Products
WHERE CategoryID = iCategoryID
ORDER BY ProductID;
END GetProductsByCategoryID;
END ProductPackage;
このサンプルでは、以下のノウハウを習得することができます。
▼RepeaterにRepeaterをネストさせる方法
▼RepeaterのItemTemplateにRepleaterを作成する方法
▼ItemTemplateにデータ連結式を記述する方法
▼WebページにJavaScriptを挿入する方法
▼<div>タグにクライアント側で動作するonclickイベントを登録する方法
▼onclickイベントにデータ連結式を記述する方法
▼JavaScriptでStyle属性のdisplayプロパティを可視、不可視に書き換える方法
1. Webフォーム追加
ソリューションエクスプローラからフォルダ[ch6]を右クリックして、新規Webフォーム「ch67TabbedControl1.aspx」を追加します。
2.
Repeater1作成
ツールボックスの[Webフォーム]からRepeaterをドラッグ&ドロップします。デザイナにRepeater1のオブジェクトが作成されます。

図 デザインにRepeater1作成
3.
Repeater1のテンプレート作成
デザイナの最下位から[HTML]タブをクリックして、HTMLビューに切り替えます。Repeater1の<asp:Repeater>...</asp:Repeater>の間にHeaderTemplate、ItemTemplate、FooterTemplateを追加します。
<asp:Repeater id="Repeater1" runat="server">
<HeaderTemplate>
<div id="container" style="width:100%;height:350px">
</HeaderTemplate>
<ItemTemplate>
<div id='h<%# DataBinder.Eval(Container, "ItemIndex")%>'
onclick='showTopic(<%# DataBinder.Eval(Container, "ItemIndex")%>);'
style="cursor:pointer;cursor:hand;position:relative;z-index:2;
border-left: solid 1px black;width:20%;background-color:#aaaaaa;
border-right: solid 1px black;border-bottom: solid 1px
black;padding:5px;">
<%# DataBinder.Eval(Container.DataItem, "CategoryName") %>
</div>
<div id='d<%# DataBinder.Eval(Container, "ItemIndex")%>'
style="overflow:auto;z-index:1;border: solid 1px black;width:80%;
position:absolute;background-color:#eeeeee;padding:5px;display:none;">
<!--ここにRepeater 2 挿入 -->
</div>
</ItemTemplate>
<FooterTemplate>
</div>
</FooterTemplate>
</asp:Repeater>
4. Repeater2作成
HTMLビューの最下位から[デザイン]タブをクリックしてデザインビューに切り替えます。ツールボックスの[Webフォーム]からRepeaterをドラッグ&ドロップします。デザイナにRepeater2のオブジェクトが作成されます。

図 デザイナにRepeater2作成
プロパティウィンドウから[Repeater2]を選択したら、「(DataBindings)」プロパティを選択して
をクリックします。「Repeater2データ連結」が表示されたら、「連結可能プロパティ」から[DataSource]を選択します。「DataSourceの連結」から[カスタム連結式]を選択したら、テキストボックスに以下の連結式を入力します。
GetProductsByCategoryID(DataBinder.Eval(Container.DataItem, "CategoryID"))
[OK]をクリックして「データ連結」を閉じます。
5.
Repeater2のテンプレート作成
デザイナの最下位から[HTML]タブをクリックして、HTMLビューに切り替えます。Repeater2の<asp:Repeater> ...</asp:Repeater>の間にHeaderTemplate、ItemTemplate、FooterTemplateを追加します。
<asp:Repeater id="Repeater2" runat="server"
DataSource='<%# GetProductsByCategoryID(DataBinder.Eval(Container.DataItem, "CategoryID")) %>'>
<HeaderTemplate>
<ul>
</HeaderTemplate>
<ItemTemplate>
<li>
<%# DataBinder.Eval(Container.DataItem, "ProductName") %>
<%# DataBinder.Eval(Container.DataItem, "UnitPrice", " ({0:c0})") %>
</li>
</ItemTemplate>
<FooterTemplate>
</ul>
</FooterTemplate>
</asp:Repeater>
<asp:Repeater id="Repeater2">...</asp:Repeater>を選択したら、右クリックから[切り取り]を選択します。マウスを「<!--ここにRepeter2挿入 -->」の直後に移動したら、右クリックから[貼り付け]を選択します。これで、Repeater1にRepeater2がネストされます。
6.
JavaScript挿入
HTMLビューの<head>...</head>の間に、以下のJavaScriptを追加します。
<script language="JavaScript">
var lastSelectedNum = -1;
var totalCount = <%=mintTotalCount%>;
function showTopic(num) {
if (lastSelectedNum >= 0) {
var lastHeader = document.getElementById('h' + lastSelectedNum);
var lastDetails = document.getElementById('d' + lastSelectedNum);
lastHeader.style.backgroundColor = '#aaaaaa';
lastHeader.style.borderRightStyle = 'solid';
lastDetails.style.display = 'none';
}
var header = document.getElementById('h' + num);
var details = document.getElementById('d' + num);
header.style.backgroundColor = '#eeeeee';
header.style.borderRightStyle = 'none';
details.style.display = 'inline';
lastSelectedNum = num;
}
function setInitialPositions() {
var topHeader = document.getElementById('h0');
var container = document.getElementById('container');
var maxHeight = -1;
topHeader.style.borderTopStyle = 'solid';
topHeader.style.borderTopColor = 'black';
topHeader.style.borderTopWidth = '1px';
for (var i = 0; i < totalCount; i++){
var details = document.getElementById('d' + i);
details.style.top = getAscendingTops(container);
details.style.left = getAscendingLefts(container) + topHeader.clientWidth + 1;
details.style.height = '100%';
if (!container.height) {
details.style.display = 'inline';
if (details.clientHeight > maxHeight) maxHeight = details.clientHeight;
details.style.display = 'none';
}
}
if (!container.height){
container.style.height = maxHeight + 5;
}
}
function getAscendingTops(elem){
if (elem==null)
return 0;
else {
return elem.offsetTop+getAscendingTops(elem.offsetParent);
}
}
function getAscendingLefts(elem){
if (elem==null)
return 0;
else {
return elem.offsetLeft+getAscendingLefts(elem.offsetParent);
}
}
</script>

図 <head>...</head>タグの間にJavaScript挿入
</form>タグの直前に以下のJavaScriptを追加します。
<script language="JavaScript">
setInitialPositions();
showTopic(0);
</script>

図 </form>タグの直前にJavaScript挿入
7. コードビューに切り替え
メニューバーから[表示]→[コード]を選択してコードビューに切り替えます。クラスモジュールの先頭に、以下のImportsステートメントを追加します。
Imports System.Data
Imports Oracle.DataAccess.Client
Imports Oracle.DataAccess.Types
Page_Loadイベントの直前に、変数mintTotalCountを追加します。
Public mintTotalCount As Integer
Page_Loadイベントに、以下のコードを追加します。
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs)
Handles MyBase.Load
BindCategories()
End Sub
クラスモジュールの最後に、Sub BindCategories、Function GetCategories、Function GetProductsByCategoryIDを追加します。
Private Sub BindCategories()
Repeater1.DataSource = GetCategories()
Repeater1.DataBind()
mintTotalCount = Repeater1.Items.Count
End Sub
Private Function GetCategories() As OracleDataReader
Dim con As New OracleConnection(ConfigurationSettings.AppSettings("conStringOraNw"))
Dim cmd As New OracleCommand("CategoryPackage.GetCategories", con)
con.Open()
With cmd
.CommandType = CommandType.StoredProcedure
.Parameters.Add("1", OracleDbType.RefCursor, ParameterDirection.Output)
End With
Return cmd.ExecuteReader(CommandBehavior.CloseConnection)
End Function
Public Function GetProductsByCategoryID(ByVal intCategoryID As Integer) As DataTable
Dim con As New OracleConnection(ConfigurationSettings.AppSettings("conStringOraNw"))
Dim da As New OracleDataAdapter
Dim cmd As New OracleCommand("ProductPackage.GetProductsByCategoryID", con)
Dim dt As New DataTable
With cmd
.CommandType = CommandType.StoredProcedure
.BindByName = True
.Parameters.Add("orcurProducts", OracleDbType.RefCursor, ParameterDirection.Output)
.Parameters.Add("iCategoryID", OracleDbType.Int32).Value = intCategoryID
End With
da.SelectCommand = cmd
da.Fill(dt)
Return dt
End Function
8. ブラウザに表示
ソリューションエクスプローラから[ch67TabbedControl1.aspx]を右クリックしてブラウザに表示します。Repeater1にCategories表(商品区分)が表示されます。Repeater1から商品区分をクリックすると、Repeater2に商品区分に該当するProducts表(商品)が表示されます。

図 商品区分をクリックすると該当する商品が表示される
■ 解説
このサンプルはRepeater1のItemTemplateにRepeater2をネストさせて、商品区分別の商品をタブコントロール形式で表示しています。
<asp:Repeater id="Repeater1" runat="server">
<HeaderTemplate>...</HeaderTemplate>
<ItemTemplate>
<asp:Repeater id="Repeater2"
runat="server">
<HeaderTemplate>...</HeaderTemplate>
<ItemTemplate>...</ItemTemplate>
<FooterTemplate>...</FooterTemplate>
</asp:Repeater>
</ItemTemplate>
<FooterTemplate>...</FooterTemplate>
</asp:Repeater>
Page_Loadイベントでは、BindCategoriesメソッドを実行してRepeater1にCategories表のDataReaderをバインドします。
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs)
Handles MyBase.Load
BindCategories()
End Sub
BindCategoriesメソッドは、GetCategoriesメソッドを実行してCategories表のDataReaderオブジェクトを作成します。Repeater1オブジェクトのDataSourceプロパティにCategories表のDataReaderオブジェクトを設定したら、DataBindメソッドを実行してバインドします。Repeater1のDataBindメソッドを実行すると、Repeater2のDataBindメソッドも自動的に実行されます。
Private Sub BindCategories()
Repeater1.DataSource = GetCategories()
Repeater1.DataBind()
mintTotalCount = Repeater1.Items.Count
End Sub
GetCategoriesメソッドは、OracleCommandオブジェクトのExecuteReaderメソッドを実行して、Categories表のDataReaderオブジェクトを生成して返します。ExecuteReaderメソッドの引数に、CommandBehavior.CloseConnectionを指定すると、DataReaderを閉じたときにOracleConnectionのCloseメソッドを自動的に実行します。
Private Function GetCategories() As OracleDataReader
Dim con As New OracleConnection(ConfigurationSettings.AppSettings("conStringOraNw"))
Dim cmd As New OracleCommand("CategoryPackage.GetCategories", con)
con.Open()
With cmd
.CommandType = CommandType.StoredProcedure
.Parameters.Add("1", OracleDbType.RefCursor, ParameterDirection.Output)
End With
Return cmd.ExecuteReader(CommandBehavior.CloseConnection)
End Function
Repeater1のItemTemplateには、データ連結式を記述してCategories表のCategoryName(商品区分名)をバインドしています。<div>タグのid属性に「id='h<%# DataBinder.Eval(Container, "ItemIndex")%>'」のようにデータ連結式を記述して「id=h1, id=h2, id=h3...」のようなidを生成します。
同様に、「id='d<%# DataBinder.Eval(Container, "ItemIndex")%>'」のデータ連結式は、「id=d1, id=d2, id=3...」のようなidを生成します。
<div>タグのidは、Repeater2を可視、不可視に切り替えるときに参照します。Repeater2を可視状態に切り替えるには、<div>タグのstyle属性のdisplayプロパティに「inline」を設定します。Repeater2を不可視状態に切り替えるには、displayプロパティに「none」を設定します。
Repeater1の「商品区分名」をクリックしたときに、Repeater2を可視状態に切り替えるには、<div>タグにクライアント側のブラウザで実行されるonclickイベントと登録します。
<div id='h<%# DataBinder.Eval(Container, "ItemIndex")%>'
onclick='showTopic(<%#
DataBinder.Eval(Container, "ItemIndex")%>);'
style="...">
Repeater1の「商品区分名」をクリックすると、onclickイベントが発生してJavaScriptのshowTopic()関数が実行されます。
<asp:Repeater id="Repeater1" runat="server">
<HeaderTemplate>
<div id="container" style="width:100%;height:350px">
</HeaderTemplate>
<ItemTemplate>
<div id='h<%# DataBinder.Eval(Container, "ItemIndex")%>'
onclick='showTopic(<%# DataBinder.Eval(Container, "ItemIndex")%>);'
style="...">
<%#
DataBinder.Eval(Container.DataItem, "CategoryName") %>
</div>
<div id='d<%# DataBinder.Eval(Container, "ItemIndex")%>'
style="...">
<!--ここにRepeater 2 挿入 -->
</div>
</ItemTemplate>
<FooterTemplate>
</div>
</FooterTemplate>
</asp:Repeater>
showTopic関数の引数には、<div>タグのID番号を指定します。showTopic関数は、すでに表示(可視)されている<div>タグのstyle属性のdisplayプロパティに「none」を設定して不可視にします。そして、今回選択した<div>タグのstyle属性のdisplayプロパティに「inline」を設定して可視に切り替えます。
style属性のdisplayプロパティを「none」、「inline」に切り替える処理は、クライアントのブラウザで実行しますのでWebページがポストバックされません。このように、ブラウザ側で可視/不可視の切り替えを行うと、画面のチラツキがなく使い勝手の良いユーザーインタフェースを構築することができます。ただし、JavaScriptを利用すると、ブラウザによっては正常に動作しないことがありますので適用ブラウザを明確にする必要があります。なお、このサンプルで使用しているJavaScriptは、Internet Explorer 6.xを使用することを前提としています。その他のブラウザを使用したときは、動作が保証されません。
function showTopic(num) {
if (lastSelectedNum >= 0) {
var lastHeader = document.getElementById('h' + lastSelectedNum);
var lastDetails = document.getElementById('d' + lastSelectedNum);
lastHeader.style.backgroundColor = '#aaaaaa';
lastHeader.style.borderRightStyle = 'solid';
lastDetails.style.display = 'none';
}
var header = document.getElementById('h' + num);
var details = document.getElementById('d' + num);
header.style.backgroundColor = '#eeeeee';
header.style.borderRightStyle = 'none';
details.style.display =
'inline';
lastSelectedNum = num;
}
Repeater2のDataSourceプロパティには、データ連結式を記述してProducts表のDataTableをバインドしています。GetProductsByCategoryIDメソッドは、Products表から商品区分IDに該当する行(レコード)をDataTableに格納して返します。GetProductsByCategoryIDメソッドの引数には、Categories表のCategoryID(商品区分ID)を指定します。
Repeater2のItemTemplateでは、データ連結式でProducts表のProductName(商品名)とUnitPrice(単価)を表示しています。単価をバインドするときの書式「{0:c0}」は、通貨型で小数点以下の桁数を「0」にすることを意味します。
<asp:Repeater id="Repeater2" runat="server"
DataSource='<%#
GetProductsByCategoryID(DataBinder.Eval(Container.DataItem, "CategoryID")) %>'>
<HeaderTemplate>
<ul>
</HeaderTemplate>
<ItemTemplate>
<li>
<%#
DataBinder.Eval(Container.DataItem, "ProductName") %>
<%#
DataBinder.Eval(Container.DataItem, "UnitPrice", " ({0:c0})") %>
</li>
</ItemTemplate>
<FooterTemplate>
</ul>
</FooterTemplate>
</asp:Repeater>