1レコード複数行表示をWebで容易に実現するMultiRow for ASP.NET
ASP.NETでDB上の1レコードを複数行として表示させるためには、GridViewコントロールでの実装は難しく、Repeaterコントロールを使ってHTMLの<table>タグを駆使して実装しなければなりません。
<table>タグを駆使する方法は、ASP.NET以外の方法でWebアプリを作成するときも同じようなものですが、例えば交互に背景色を切り替えて表を見やすくするときには同じ行定義を<ItemTemplate>タグと<AlternatingItemTemplate>タグに定義しなければならないなど、使い勝手の悪さが気になります。ソート機能やページ制御についても工夫をしなければなりません。
また、ASP.NETのWebフォーム用コントロールの使いやすさに慣れてしまうと、Repeaterコントロールは必ずしも使いやすいコントロールとは言えません。Windowsフォーム用にグレープシティから発売されている、MultiRowコントロールのASP.NET版でもあればと数年前から待ちわびていましたが、遂にMultiRow for ASP.NETが登場してくれました。
Repeaterコントロールを使った実装例
MultiRow for ASP.NETでどれくらい楽ができるかを知るために、まずはRepeaterコントロールを使った場合、どれくらい苦労するかを再検証してみましょう。サンプルソースにこちらのRepeaterコントロールを使った場合の完成版も同梱してあるので、参考にしてみてください(サンプルソース内「CZ1007RepeterControlSample」)。
作成する複数行表示は、図1のようになります。面倒なのでページ制御やソート機能は実装していません。
このデザインを実現するためのHTMLは、リスト1のようになります。
<form id="RepeaterControlSample_Form" runat="server">
<div>
<asp:Repeater ID="Repeater1" runat="server">
<HeaderTemplate>
<table border='1'>
<tr><th rowspan="2">日付</th><th colspan="5">製品名</th></tr>
<tr><th>伝票No</th><th>区分</th><th>単価</th><th>数量</th><th>金額z</th></tr>
</HeaderTemplate>
<ItemTemplate>
<tr style="background-color:White;">
<td rowspan="2"><%# Format(DataBinder.Eval(Container.DataItem, "Date"), "yyyy/MM/dd")%> </td>
<td colspan="5"><%# DataBinder.Eval(Container.DataItem, "Products").ToString%> </td>
</tr>
<tr style="background-color:White;">
<td><%# DataBinder.Eval(Container.DataItem, "SlipNo")%> </td>
<td><%# DataBinder.Eval(Container.DataItem, "Category")%> </td>
<td style="text-align:right;"><%# Format(DataBinder.Eval(Container.DataItem, "UnitPrice"), "#,##0")%> </td>
<td style="text-align:right;"><%# Format(DataBinder.Eval(Container.DataItem, "Number"), "#,##0")%> </td>
<td style="text-align:right;"><%# Format(DataBinder.Eval(Container.DataItem, "UnitPrice") *
DataBinder.Eval(Container.DataItem, "Number"), "#,##0")%> </td>
</tr>
</ItemTemplate>
<AlternatingItemTemplate>
<tr style="background-color:#e0ffe0;">
<td rowspan="2"><%# Format(DataBinder.Eval(Container.DataItem, "Date"), "yyyy/MM/dd")%> </td>
<td colspan="5"><%# DataBinder.Eval(Container.DataItem, "Products").ToString%> </td>
</tr>
<tr style="background-color:#e0ffe0;">
<td><%# DataBinder.Eval(Container.DataItem, "SlipNo")%> </td>
<td><%# DataBinder.Eval(Container.DataItem, "Category")%> </td>
<td style="text-align:right;"><%# Format(DataBinder.Eval(Container.DataItem, "UnitPrice"), "#,##0")%> </td>
<td style="text-align:right;"><%# Format(DataBinder.Eval(Container.DataItem, "Number"), "#,##0")%> </td>
<td style="text-align:right;"><%# Format(DataBinder.Eval(Container.DataItem, "UnitPrice") *
DataBinder.Eval(Container.DataItem, "Number"), "#,##0")%> </td>
</tr>
</AlternatingItemTemplate>
<FooterTemplate>
</table>
</FooterTemplate>
</asp:Repeater>
</div>
</form>
Repeaterコントロールのデータソースとして指定したDataSetに格納されたレコードを<td>タグで囲んで出力するために、DataBinder.Evalを使っています。デザイナの[デザイン]タブでは編集できないため、[コード]タブでHTMLを直書きして行います。
Repeaterコントロールで交互に背景色を変えるときは、<ItemTemplate>タグと<AlternatingItemTemplate>タグの中に同じ行定義を記述し、<tr>タグのStyleに指定するBackground-Colorを別の背景色にすることで実現します。
この実現方法の場合、データの配置などを変えるときは、次のような作業を行わなくてはなりません。
<HeaderTemplate>でヘッダ行の変更<ItemTemplate>でデータ行の変更- (2)の変更を
<AlternatingItemTemplate>に反映
実際、開発現場でお客様からのレビューを受けてデザイン変更していると、つい(3)のステップを忘れてしまうときがあります。私自身、実装していて非常に面倒で嫌になってしまう手順でした。
Repeaterコントロールの実行
Repeaterコントロールに値を表示するためには、リスト2のようにDataSetのテーブルをDataSourceプロパティに設定してから、DataBindメソッドを実行します。
Imports System.Data
Partial Class RepeaterControlSample
Inherits System.Web.UI.Page
Protected Sub Page_Load(ByVal sender As Object,
ByVal e As System.EventArgs) Handles Me.Load
If Not Me.IsPostBack Then
Call GetRecords()
End If
End Sub
Private Function GetRecords() As Boolean
Dim isOK As Boolean = False
Using _proc As New CZ1007Bound
Dim ds As DataSet = _proc.GetRecords("admin", "", "WS-DF502")
Me.Repeater1.DataSource = ds.Tables("Bill")
Me.Repeater1.DataBind()
isOK = True
End Using
Return isOK
End Function
End Class
次にMultiRow for ASP.NETを使った際の、実現方法を見ていきましょう。
MultiRow for ASP.NETでの効率化例
それでは、MultiRow for ASP.NETで実現する場合は、どのようになるか確認してみましょう。サンプルソースは、「CZ1007MultiRowSample」です。
そのために、まず事前準備としてツールボックスにMultiRowを追加しなければなりません。

ツールボックス上で右クリックし、[アイテムの選択]メニューを選びます。ダイアログボックスが表示されたら、名前空間「GrapeCity.Web.MultiRow」を選択して[OK]をクリックします。名前空間から分かるように、グレープシティ社が開発した製品です。
MultiRowを配置
MultiRowをWebフォームに配意するためには、ツールボックスからMultiRowのアイコンをWebフォーム上にドラッグ&ドロップします。

MultiRowをデザイン
初期状態では図3のようなレイアウトなので、MultiRowのスマートタグからタスクを使って設定を行います。MultiRowタスクでは、レコード追加や削除など基本的な操作性の設定ができます。

MultiRowの1データ分のデザインは、テンプレートをデザインすることで実現します。テンプレートをグラフィカルにデザインするための機能が「テンプレートデザイナ」です。テンプレートデザイナの起動は、MultiRowタスクから行います。
テンプレートデザイナでは、デザイン画面にツールボックスからセルをドラッグ&ドロップしてデザインを決めていきます。特筆すべき点として、セルとセルの間が0のときにスナップ線が表示されることが挙げられます。これは日本の帳票文化では非常に重要で、効率がいい操作性です。
もちろん、2行に渡るセルや2列にわたるセルも、配置した後でセルの境界線をドラッグすると自由に大きさを変更できます。何種類かセルがあるので、文字列や数値用のセルを目的に応じて選べば、それぞれの属性に合わせた修飾も可能です。
図5ではヘッダ部分のテキストを中心に表示するようになっています。これは「ColumnHeaderSection1」の部分を選択し、DefaultHeaderCellStyleの、ContentHAlignプロパティとContentVAlignプロパティで設定しています。ヘッダに配置した個々のセルでは設定は行っていません。もし、一部のヘッダのみ違った文字配置にしたいときは、その一部のヘッダセルのところだけを設定することもできます。
このように、最小限の設置で全体を制御できるのも魅力的です。

1データごとに交互に背景色を変えたいときは、プロパティウィンドウの「AlterntingRowStyle」で、別の色の設定を追加できます。
図7にあるように1行おきだけではなく、RowStyleコレクションに追加していけば、3色以上の色違いにすることもできます。
MultiRowを実行
MultiRowに値を表示するためには、リスト3のようにDataSetのテーブルを「DataSource」プロパティに設定してから、DataBindメソッドを実行します。
Imports System.Data
Partial Class MultiRowSample
Inherits System.Web.UI.Page
Protected Sub Page_Load(ByVal sender As Object,
ByVal e As System.EventArgs) Handles Me.Load
If Not Me.IsPostBack Then
Call GetRecords()
End If
End Sub
Private Function GetRecords() As Boolean
Dim isOK As Boolean = False
Using _proc As New CZ1007Bound
Dim ds As DataSet = _proc.GetRecords("admin", "", "WS-DF502")
Me.MultiRow1.DataSource = ds.Tables("Bill")
Me.MultiRow1.DataBind()
isOK = True
End Using
Return isOK
End Function
End Class
MultiRow for ASP.NETでページングを実装する
MultiRow for ASP.NETにはページングを行うためのプロパティがあります。それがAllowPagingプロパティです。AllowPagingプロパティをTrueに設定しておけばPageSizeプロパティに設定された行数ごとにページングできます。
このように設定することによりDataSourceに割り当てられたデータソースに含まれているデータ全体からカレントページの先頭位置を算出してPageSizeプロパティの行数だけを表示します。データソース全体を対象にするのでデータソース自身にはページング機能の実装などは不要です。
![図9 [ページングを有効にする]にチェックを入れる、デフォルトの設定は1ページ10行表示](http://cz-cdn.shoeisha.jp/static/images/article/5351/12.gif)
MultiRow for ASP.NETでObjectDataSourceのページングを利用する
取り扱うデータが膨大でパフォーマンスが気になる場合は、少し手間がかかりますが、ObjectDataSourceのページング機能を併用することで、高パフォーマンスなページの作成が可能です(サンプルソース「CZ1007PagerSample」)。

今回、データベースからの値の入出力は、すべて「CZ1007Bound」クラスで実装しています。そこで、ObjectDataSourceを介してページングに必要な動作を行うメソッドを「CZ1007Bound」クラスに追加し、ページング機能付きMultiRowを実現してみましょう。
ページング機能付きデータアクセスクラス
「CZ1007Bound」クラスにページング機能を実装するためには、いくつかの約束事を守ったメソッドを実装しなければなりません。
必要なメソッドは「レコード数を取得する」メソッドと、「先頭から数えてn個目からm個のレコードを取得する」メソッドの2つです。この2つのメソッドのパラメタは、同一にしなければなりません。
今回のサンプルでは、「レコード数を取得する」メソッドとしてGetNumberOfOrdersメソッドを実装します。本来は先頭3つのパラメタだけで十分ですが、ObjectDataSourceで使われることを前提として、startRowIndexとmaximumRowsパラメタが追加されています。
Public Function GetNumberOfOrders(ByVal userID As String,
ByVal password As String,
ByVal billNo As String,
ByVal startRowIndex As Integer,
ByVal maximumRows As Integer) As Integer
Dim rows As Integer = 0
Using _cn As New OleDb.OleDbConnection()
_cn.ConnectionString = String.Format(System.Configuration.ConfigurationManager.AppSettings("ConnectionString"),
userID,
password)
_cn.Open()
Using _cmd As New OleDb.OleDbCommand
_cmd.Connection = _cn
_cmd.CommandText =
"SELECT COUNT(*) " &
"FROM Customers " &
"INNER JOIN (Bill INNER JOIN BillCondition " &
"ON Bill.BillNo = BillCondition.BillNo)" &
"ON Customers.CustomerID = Bill.CustomerID " &
"WHERE Bill.BillNo=? "
_cmd.Parameters.AddWithValue("@billNo", billNo)
rows = CType(_cmd.ExecuteScalar, Integer)
End Using
_cn.Close()
End Using
Return rows
End Function
「先頭から数えてn個目からm個のレコードを取得する」メソッドとしては、GetRecordsByIndexメソッドを実装します。nとmは、それぞれstartRowIndexとmaximumRowsというパラメタ値として、このメソッドに渡ってきます。
Public Function GetRecordsByIndex(ByVal userID As String,
ByVal password As String,
ByVal billNo As String,
ByVal startRowIndex As Integer,
ByVal maximumRows As Integer) As DataSet
Dim ds As New DataSet
Using _cn As New OleDb.OleDbConnection()
:
(中略)
:
End Using
Return ds
End Function
ページング機能付きデータアクセスクラスをObjectDataSourceで利用
「CZ1007Bound」クラスをObjectDataSourceで利用するために、設定が必要なメソッドとしては次のものがあります。
| プロパティ | 用途 | 設定値 |
|---|---|---|
| TypeName | クラス名を指定 | CZ1007Bound |
| SelectMethod | レコード取得用メソッド名 | GetRecordsByIndex |
| SelectCountMethod | 件数取得用メソッド名 | GetNumberOfOrders |
| EnablePaging | True:ページング | True |
ObjectDataSourceに対してページング機能を有効(EnablePaging=True)にすると、StartRowIndexParameterNameメソッドとMaximumRowsParameterNameメソッドの設定値も有効になります。
StartRowIndexParameterNameメソッドは、カレントページの先頭に表示する行位置を「CZ1007Bound」クラスに渡すためのパラメタ名で、デフォルト値として「startRowIndex」が指定されています。
MaximumRowsParameterNameメソッドは、ページ内の行数を「CZ1007Bound」クラスに渡すためのパラメタ名で、デフォルト値として「maximumRows」が指定されています。
ここで指定したパラメタ名をSelect時に使うためのは、パラメータコレクションエディターを使います。

このように設定しておけば、リスト3に相当するコードを記述しなくても、「CZ1007Bound」クラスのメソッドが自動的に呼び出されてデータを取得します。そのため「aspx.vb」ファイルの内容は、リスト6のように初期状態のままになっています。
Partial Class MultiRowSample
Inherits System.Web.UI.Page
End Class
実行
フッタのページ数をクリックすれば、該当ページに移動もできます。クリックするたびにパラメタにも適切な値が設定されて、「CZ1007Bound」クラスのGetRecordsByIndexメソッドが自動的に呼び出されます。
次回予告
今回は参照画面について、Repeaterコントロールよりも簡単な使い方を紹介しました。次回は、MultiRowを使った更新画面など、さらに詳細な使い方を応用編として紹介します。






