ヒント6:ASP.NET 2.0のGridViewの概要
ブラウザベースのデータベースアプリケーションでは、多くの場合、GridViewコントロールの機能を利用します。GridViewコントロールは、DataGridコントロールの後継にあたる、ASP.NET 2.0の新機能です。Windowsフォーム環境からWebフォーム環境に移ってきた開発者は、両プラットフォーム間でGridViewコントロールが大きく異なることに気付くはずです。ヒント6~9では、その一般的な機能について説明します。
図2は、ASP.NETの基本的なGridViewの例です。この例では、Northwindデータベースからデータを取得し、結果のデータセットにGridViewをバインドします。そして、列、配置、書式を定義して、運賃が100.00ドルを超える行を黄色で表示します。
この実装には、次の5つの手順を踏みます。
まず、WebフォームのツールボックスからWebフォームページにGridViewコントロールをドラッグ&ドロップします。WebフォームデザイナでGridViewコントロールのインスタンスがページ上に作成されたら、GridViewを右クリックしてプロパティページを表示します。名前(ID)をgrdOrdersに変更し、AllowPagingをtrueに、AllowSortingをtrueに、AutoGenerateColumnsをfalseに、それぞれ設定します。背景色を交互に変更させたい場合は、AlternatingRowStyleプロパティを展開し、BackColorを目的の背景色に設定します。
2つ目は、列を手動で定義する処理です。これが必要なのは、AutoGenerateColumnsプロパティをfalseに設定したためです。プロパティシートで[Columns]コレクションを選択すると、列を定義できるダイアログが表示されます(図3を参照)。このダイアログでは、バインドフィールドを追加します。それには、[BoundField]を選択して強調表示し、[Add(追加)]をクリックして、右側でDataFieldプロパティを設定します。また、ユーザーによる並べ替えを可能にする列については、SortExpressionプロパティも定義します。
図2のGridViewの表示では、一部の列が右揃えになっています。また、2つの日付列(「Order Date」と「Ship Date」)の書式が異なっています。一方は日付のみなのに対し、もう一方は日付に加えてHH:MM形式の時刻も表示されています。
図3で、バインドされる個々の列を追加したら、HeaderStyleおよびItemStyleの両グループプロパティのHorizontalAlignプロパティを設定できます。日付の書式はDataFormatStringプロパティで設定します。値は、日付のみの場合は{0:d}、日付と時刻(秒以外すべて)の場合は{0:g}です。なお、DataFormatStringを使用する列では、HtmlEncodeプロパティをfalseに設定する必要があります。これは、クロスサイトスクリプティングを防ぐための、ASP.NET 2.0の新しいセキュリティ機能に伴うものです。
GridViewのデザインが済んだら、このGridViewをバインドするためのコードを記述します。
DataSet dsData = this.GetData(); // Run whatever process you want, for the query // SELECT OrderID, CustomerID, OrderDate, // ShippedDate, Freight FROM Orders ORDER BY // OrderID this.grdOrders.DataSource = dsData.Tables[0]; this.grdOrders.DataBind();
最後に、運賃が100ドルを超える行を強調表示する機能を実装します。それには、GridViewのRowCreatedイベントを使用します。この中では、GridViewの現在の行をDataRowViewオブジェクトにキャストできます。運賃の値を判断し、背景色を設定します。
protected void grdOrders_RowCreated(object sender, GridViewRowEventArgs e) { if (e.Row.DataItem != null) { DataRowView drv = (DataRowView)e.Row.DataItem; decimal nFreight = Convert.ToDecimal(drv["Freight"]); if (nFreight> 100) e.Row.BackColor = System.Drawing.Color.Yellow; } }
なお、本番環境用のコードでは、運賃のロジックはビジネス層にまとめるのが良いでしょう。また、交互表示の色は、ハードコーディングするのではなくCSSの定義を使用するとよいでしょう。
ヒント7:GridViewでの選択処理
GridViewを使い始めて日が浅い人から、GridViewの特定の行にリンクを配置する方法を聞かれることがあります。例えば、その行の詳細情報が掲載された別のWebページを開くなどの処理を行う場合です(図4を参照)。
これを実装する方法はいくつかあります。ここでは、3つの簡単な手順で済む方法を紹介します。手順1では、GridViewの列デザイナを開き、使用可能なフィールドのリストからButtonFieldを追加します(BoundFieldではありません)。ButtonTypeプロパティは「Link」のままとし、Textを「Open」など目的のテキストに設定して、CommandNameを「Select」に設定します。
手順2は後回しにして、先に手順3を説明します。手順3では、GridViewのSelectedIndexChangedイベントを使用して、ユーザーがクリックした「Open」リンクに対応する行を判定する処理を追加します。具体的にどうすれば行を判定できるのか、疑問に思うかもしれません。GridViewにはSelectedDataKeyというデザイン時プロパティがあり、一意の識別子を設定できます。今回は、このプロパティにOrderIDを割り当てるという処理を手順2で行います。そして、手順3では、SelectedIndexChangedイベントの中でSelectedDataKeyの値を判定するというわけです。
protected void grdOrders_SelectedIndexChanged (object sender, EventArgs e) { int nOrderID = (int) this.grdOrders.SelectedDataKey.Values ["OrderID"]; // Now we can load another page with the ID }
この方法に関して、1つ補足しておきます。テキストリンクではなく、「開く」や「編集」を意味するアイコンを表示したい場合、基本的な手順は上記と同じですが、ButtonTypeを「Link」ではなく「Image」とし、ImageURLには、表示する画像のURLを設定します。
ヒント6では、条件に応じて行の色を変える方法を紹介しました。場合によっては、条件に応じてGridViewにアイコンを表示するという処理が必要になるかもしれません。それを実現するには、GridViewに特別なTemplateField列を定義する必要があります。それには、WebページのHTMLソースを開き、GridViewの先頭(または末尾)の列として次のコードを追加します。
<asp:TemplateField > <ItemTemplate> <img src= '<%# GetPic(Container.DataItem) %>'/> </ItemTemplate> <ItemStyle Width="3px" /> <HeaderStyle Width="3px" /> </asp:TemplateField>
関数GetPicはカスタムコードです。この関数では、GridViewの現在の行をパラメータとして受け取り、使用するアイコンの画像の名前を返します。返すアイコンは、行が所定の条件を満たす場合は目的のアイコンとし、満たさない場合は空白のgifファイルにします。空文字列を返すことはできないという点に注意してください。GridViewの該当するセルに赤の×印が表示されてしまい、見苦しくなるからです。
protected string GetPic(object dataitem) { string cIcon = ""; // evaluate the row from the GridView bool lFlag = Convert.ToBoolean(DataBinder.Eval (dataitem, "ShowPictureFlag")); if (lFlag == true) cIcon = "SpecialPic.gif"; else cIcon = "Blank.gif"; return cIcon; }
ヒント8:ASP.NET 2.0のGridViewでの並べ替え
ヒント6で作成した基本的なGridViewでは、AllowSortingプロパティとAllowPagingプロパティをtrueに設定し、データバインドされた各列に対してSortExpressionを定義しました。このGridViewをWebブラウザで動作させると、各列見出しはリンクとして表示されます。グリッドをその列で並べ替えるときにクリックするためのリンクです。また、グリッドの下部にもリンクが表示されます(AllowPagingプロパティによるものです)。こちらは、バインドされたデータセットの全行が単一のページでは収まらない場合に、別のページに移動するためのリンクです。
しかし悲しいかな、実際のところは、これらのリンクのどれをクリックしても、期待どおりには動いてくれず、実行時エラーのメッセージが表示されてしまいます(図5と図6を参照)。
何がいけないかと言うと、メッセージからも分かるように、処理を正常に進めるためには、GridViewのSortingイベントとPageIndexChangingイベントにコードを追加する必要があるのです。これらのイベントはポストバックの中で発生されるので、少し前の手順に戻って、処理方法全体を見直してみる必要があります。
SortingイベントとPageIndexChangingイベントのいずれの処理でも、基本的には、GridViewのDataSourceプロパティにアクセスして、DataSource.DefaultViewプロパティの並べ替え式を変更する必要があります。しかし、ヒント6の基本的なコードを見直してみると、アクセス可能な方法でデータソースを格納していません。従って、独自の並べ替えやページング処理のコードを実装する前に、まずはその点に対処する必要があります。
方法の1つは、GridViewのDataSource値をViewStateにプロパティとして持たせるという方法です。次のような形です。
DataTable dtOrders
{
get { return (DataTable)ViewState["dtOrders"]; }
set { ViewState["dtOrders"] = value; }
}
しかしこれでは、結果セットが巨大な場合でも、注文のリスト全体をページのViewStateに持たせることになってしまいます(これを確認するには、Webページをセットアップし、結果セットをViewStateに格納したうえで、ページを実行し、[表示]の[ソース]をクリックしてみましょう。データの量は予想よりずっと多いかもしれません)。それに、データ型によってはシリアル化できないものもあります。例えば、DataRowオブジェクトをこの方法で格納することはできません。
別の方法としては、データのバインドを実行する時点でDataSourceをセッション変数に格納するという手もあります。再び、ヒント6のコードを基にすると、次のようになります。
DataSet dsData = this.GetData(); // Run whatever process you want, for the query // SELECT OrderID, CustomerID, OrderDate, // ShippedDate, Freight FROM Orders ORDER BY // OrderID this.grdOrders.DataSource = dsData.Tables[0]; // Store the datatable as a Session Variable Session["dtOrders"] = dsData.Tables[0]; this.grdOrders.DataBind();
こうして、DataSourceをセッションに格納しておけば、Sortingイベントでセッション変数にアクセスでき、データソースのDefaultViewのSortExpressionを適切に変更できます。このSortExpressionは、列に対して最初に定義したSortExpressionプロパティからのものです。
protected void grdOrders_Sorting (object sender, GridViewSortEventArgs e) { DataTable dtOrders = (DataTable)Session["dtOrders"]; if (dtOrders != null) { dtOrders.DefaultView.Sort = e.SortExpression this.grdOrders.DataSource = dtOrders.DefaultView; Session["dtOrders"] = dtOrders; this.grdOrders.DataBind(); } }
これは、前代未聞の画期的な手法というわけではありません。ASP.NETに関するあちこちのフォーラムで、同様の手法を目にすることができます。
ヒント9:ASP.NET 2.0のGridViewでのページング
並べ替えの問題が片づいたので、次はページングの処理に移りましょう。ユーザーがページ下部のページリンクのいずれかをクリックすると、ポストバックが生じ、PageIndexChangingイベントが発生します。ユーザーが選択したページはGridViewPageEventArgsパラメータで把握でき、その値に基づいてGridViewのPageIndexを設定します。また、それまでに行われた並べ替えを維持するために、GridViewのDataSourceのDefaultViewプロパティにアクセスする必要があります。
protected void grdOrders_PageIndexChanging (object sender, GridViewPageEventArgs e) { // must set new PageIndex and rebind this.grdOrders.PageIndex = e.NewPageIndex; this.grdOrders.DataSource = ((DataTable)Session["dtOrders"]).DefaultView; this.grdOrders.DataBind(); }
さて、次の話題へ進む前に、セッション変数についてひとこと。セッション変数を使うかどうかは、人によってさまざまです。愛用して頼りきりになる人もいるし、スケーラビリティの問題から異議を唱える人もいるし、その中間の人もいます。
セッション変数を使うのは、簡単すぎるくらい簡単です。そして言うまでもなく、簡単すぎるものは、乱用される傾向が大です。『Visual Studio Magazine』誌の2003年10月号に、セッション状態の管理について、Leonard Lobel氏による素晴らしい記事が掲載されています。その中では、サーバー側で状態管理を行う7つの手法が取り上げられており、セッション情報をSQL Serverに格納する方法も紹介されています。このトピックについて理解を深めたい方には、Lobel氏の記事は大いに役立つものと思います。





