.NET TIPS

[ASP.NET]DataGridコントロールを階層表示させるには?

デジタルアドバンテージ
2004/03/12

 DataGridコントロールでデータの一覧を表形式で表示するときに、各行のデータがさらに詳細なデータの一覧を持っていて、それも同時に表示したい場合がある。このようなデータの表示は、次の画面のようにDataGridコントロールを階層化して利用することで実現できる。

DataGridコントロールを階層表示するサンプル・プログラム(nesteddg1.aspx)の実行画面
この画面では、フォーラムの一覧と、各フォーラムで公開されている記事の一覧を階層的に表示している。

 この画面は、今回作成するサンプル・プログラムの実行画面である。ここでは、Build Insiderにあるいくつかのフォーラムの一覧をグリッド表示するとともに、各フォーラムで公開されている最新記事を入れ子になった(ネストした)データグリッドで表示している。

 本稿では、このようなDataGridコントロールの階層表示の方法について解説する。ここでポイントとなるのは、階層表示するためのデータの準備と、入れ子になっているDataGridコントロールのデータソースの設定である。

階層表示に使用する「マスター/詳細データ」

 階層表示が必要となるようなデータは、一般的に「マスター/詳細データ」や「親子データ」と呼ばれるものだ。例えば、注文データと、注文ごとの注文詳細データは「マスター・データ」と「詳細データ」の関係になっている。それぞれのデータがデータベースのテーブルに格納されている場合には、それらは「マスター/詳細テーブル」と呼ばれる。

 通常、これら2つのテーブル間には「リレーション」が設定される。例えば、注文テーブルの主キーとして「注文番号」列を作成したとすると、注文詳細テーブルにも、その親データを示す注文番号を格納するための列を作成し、それを外部キーとして設定する。

 ADO.NETのデータセットは、データセットに格納されているデータテーブル間のリレーション(リレーションシップとも呼ばれる)を設定する機能を持っている。データセットにリレーションを作成するには、2つのデータテーブルの列とリレーション名を指定してDataRelationクラス(System.Data名前空間)のオブジェクトを作成し、このオブジェクトをデータセットのRelationsプロパティに追加すればよい。このRelationsプロパティはDataRelationオブジェクトのコレクションである。

 ここでは、DataGridコントロールに階層表示するためのマスター/詳細データとしてBuild Insiderで提供しているRSSデータをデータセットに読み込んで利用してみる。

 次のコードは、Build InsiderのWebカテゴリSmall & MediumカテゴリColumnカテゴリの3つのフォーラムのそれぞれの最新記事を記述したRSS情報をデータセットに読み込む。

DataSet ds = new DataSet();
ds.ReadXml("http://www.buildinsider.net/web/rss");
ds.ReadXml("http://www.buildinsider.net/small/rss");
ds.ReadXml("http://www.buildinsider.net/column/rss");

 詳細は割愛するが、この結果としてデータセットには、4つのデータテーブルと3つのリレーションが自動的に作成される。ここではこのうち、次の図に示した2つのデータテーブルと、それらのテーブル間をリンクする「channel_item」と名付けられたリレーションを利用する。

@IT(※コードではBuild Insiderになっている)のRSS情報をデータセットで読み込んで得られるデータテーブル
DataSetクラスのReadXmlメソッドを使用すると、XMLファイルを読み込んでデータテーブルを作成できる。なお実際には、この図で示していない列も各データテーブルには含まれている。本稿ではここに挙げた列のみを扱う。

 「channel_Id」はフォーラムごとに振られている番号である。ここでは3つのフォーラムについてのRSSファイルを読み込んだので、channelデータテーブルには3つのフォーラムについてのレコードが含まれている。また、itemデータテーブルには各フォーラムでの最新記事についてのレコードが含まれている。

 このようなリレーションを持つ2つのデータテーブルでは、親データテーブルにある行(DataRowオブジェクト)のGetChildRowメソッドにより、子データテーブルの行が取得できる。また、親データテーブルのデータビューなどから、行をDataRowViewオブジェクトとして取得した場合には、CreateChildViewメソッドにより子データテーブルのデータビューを取得することができる。

ネストしたDataGridコントロールの記述

 次に、階層構造になっている2つのDataGridコントロールを<asp:DataGrid>要素により定義する。これはテンプレート列を用いて、単純にその列内で入れ子となるもう1つの<asp:DataGrid>要素を記述すればよい。大まかな骨組みは次のようになる。

<asp:DataGrid id="MyGrid" ……>
   <Columns>

     <asp:BoundColumn ……>
     <asp:BoundColumn ……>

     <asp:TemplateColumn ……>
       <ItemTemplate>

         <asp:DataGrid id="MySubGrid" ……>
           <Columns>
             <asp:BoundColumn ……>
             <asp:BoundColumn ……>
           </Columns>
         </asp:DataGrid>

       </ItemTemplate>
     </asp:TemplateColumn>
   </Columns>
</asp:DataGrid>

 テンプレート列の利用については「TIPS:[ASP.NET]DataGridコントロールの行に通し番号を付けるには?」などで解説している。

入れ子になったDataGridコントロールのデータ連結

 最後にDataGridコントロールにデータセット内のテーブルをデータ連結する。外側の親DataGridコントロールには、単純に親データテーブルをデータソースに指定してデータ連結をすればよいが、問題は入れ子になった子DataGridコントロールのデータソースである。

 子DataGridコントロールのデータソースには、親の各行にデータ連結しているデータソース内の要素から、リレーションをたどって得られる子データテーブルを設定する必要がある。このような処理には、それぞれの行のデータ連結時に発生するItemDataBoundイベントを利用できる(このイベントについては「TIPS:[ASP.NET]DataGridコントロールですべての行にアクセスするには?」で解説している)。

 このItemDataBoundイベントのイベント・ハンドラとなるメソッドは次のようになる。

void MyGrid_ItemBound(object sender, DataGridItemEventArgs e) {
  if (e.Item.ItemType == ListItemType.Item
      || e.Item.ItemType == ListItemType.AlternatingItem) {

    DataGrid dg = (DataGrid)e.Item.FindControl("MySubGrid");
    dg.DataSource = GetChild(e.Item.DataItem, "channel_item");
    dg.DataBind();
  }
}

 このメソッドでは、「e.Item」が親DataGridコントロールの行を示しているDataGridItemオブジェクトであるが、まずこのオブジェクトの子コントロールとなっている子DataGridコントロールをFindControlメソッドにより取得する。次に、行にデータ連結しているデータソース内の要素(データソースがデータテーブルの場合には、これはDataRowViewオブジェクトである)から、子データテーブルのデータビューを取得する。この処理は次に示すGetChildというメソッドで記述している。子データテーブルのデータビューが得られれば、それを子DataGridコントロールにデータ連結する。

 DataRowViewオブジェクトとして得られるデータソース内の要素から、子データテーブルのデータビューを得るためのGetChildメソッドは次のようになる。

DataView GetChild(object item, string relName) {
  DataRowView drv = (DataRowView)item;
  return drv.CreateChildView(relName);
}

 ここでは前述したDataRowViewオブジェクトのCreateChildViewメソッドを呼び出している。

DataGridコントロールを階層表示するサンプル・プログラム

 ここまでに3つのポイントについて解説したが、次に示すサンプル・プログラムは、それらを組み合わせて作成したものだ。このプログラムの実行画面はすでに冒頭で示している。

<%@ Page Language="C#" EnableViewState="false" %>
<%@ Import Namespace="System.Data" %>

<html>
<head>
  <script runat="server">
    void Page_Load(object sender, EventArgs e) {

      DataSet ds = new DataSet();

      ds.ReadXml("http://www.buildinsider.net/web/rss");
      ds.ReadXml("http://www.buildinsider.net/small/rss");
      ds.ReadXml("http://www.buildinsider.net/column/rss");

      MyGrid.DataSource = ds.Tables["channel"];
      MyGrid.DataBind();
    }

    void MyGrid_ItemBound(object sender, DataGridItemEventArgs e) {
      if (e.Item.ItemType == ListItemType.Item
          || e.Item.ItemType == ListItemType.AlternatingItem) {

        DataGrid dg = (DataGrid)e.Item.FindControl("MySubGrid");
        dg.DataSource = GetChild(e.Item.DataItem, "channel_item");
        dg.DataBind();
      }
    }

    DataView GetChild(object item, string relName) {
      DataRowView drv = (DataRowView)item;
      return drv.CreateChildView(relName);
    }
  </script>
</head>

<body>
  <asp:DataGrid id="MyGrid"
      AutoGenerateColumns="false"
      OnItemDataBound="MyGrid_ItemBound"
      CellPadding="4"
      runat="server" >

    <HeaderStyle BackColor="#BB2255" ForeColor="white" />
    <ItemStyle   BackColor="#FFEEEE" />
    <AlternatingItemStyle BackColor="#FFDDDD" />

    <Columns>
      <asp:BoundColumn
          DataField="title" ItemStyle-Width="100"
          HeaderText="フォーラム名" />
      <asp:BoundColumn
          DataField="description" ItemStyle-Width="100"
          HeaderText="概要" />

      <asp:TemplateColumn HeaderText="最新記事一覧">
        <ItemTemplate>

          <asp:DataGrid id="MySubGrid"
              AutoGenerateColumns="false"
              CellPadding="4"
              runat="server" >

            <HeaderStyle BackColor="#5522BB" ForeColor="white" />
            <ItemStyle   BackColor="#EEEEFF" />
            <AlternatingItemStyle BackColor="#DDDDFF" />

            <Columns>
              <asp:BoundColumn
                  DataField="title" HeaderText="タイトル" />
              <asp:BoundColumn
                  DataField="pubDate" HeaderText="日付" />
            </Columns>
          </asp:DataGrid>

        </ItemTemplate>
      </asp:TemplateColumn>
    </Columns>
  </asp:DataGrid>
</body>
</html>
DataGridコントロールを階層表示するC#のサンプル・プログラム(nesteddg1.aspx)

DataSource属性を利用した子DataGridコントロールのデータ連結

 上記のサンプル・プログラムでは、プログラム・コードでDataGridコントロールのデータソースを設定したが、DataGridコントロールの定義時にDataSource属性を使用してもデータソースは設定可能だ。この方法を使うと、ItemDataBoundイベントを利用しないで子DataGridコントロールに子データテーブルを連結することができる。

 この方法を使って上記のサンプル・プログラムを書き換えると次のようになる。

<%@ Page Language="C#" EnableViewState="false" %>
<%@ Import Namespace="System.Data" %>

<html>
<head>
  <script runat="server">
    void Page_Load(object sender, EventArgs e) {

      DataSet ds = new DataSet();

      ds.ReadXml("http://www.buildinsider.net/web/rss");
      ds.ReadXml("http://www.buildinsider.net/small/rss");
      ds.ReadXml("http://www.buildinsider.net/column/rss");

      MyGrid.DataSource = ds.Tables["channel"];
      MyGrid.DataBind();
    }

    DataView GetChild(object item, string relName) {
      DataRowView drv = (DataRowView)item;
      return drv.CreateChildView(relName);
    }
  </script>
</head>

<body>
  <asp:DataGrid id="MyGrid"
      AutoGenerateColumns="false"
      CellPadding="4"
      runat="server" >

    <HeaderStyle BackColor="#BB2255" ForeColor="white" />
    <ItemStyle   BackColor="#FFEEEE" />
    <AlternatingItemStyle BackColor="#FFDDDD" />

    <Columns>
      <asp:BoundColumn
          DataField="title" ItemStyle-Width="100"
          HeaderText="フォーラム名" />
      <asp:BoundColumn
          DataField="description" ItemStyle-Width="100"
          HeaderText="概要" />

      <asp:TemplateColumn HeaderText="最新記事一覧">
        <ItemTemplate>

          <asp:DataGrid id="MySubGrid"
              AutoGenerateColumns="false"
              CellPadding="4"
              DataSource='<%# GetChild(Container.DataItem, "channel_item") %>'
              runat="server" >

            <HeaderStyle BackColor="#5522BB" ForeColor="white" />
            <ItemStyle   BackColor="#EEEEFF" />
            <AlternatingItemStyle BackColor="#DDDDFF" />

            <Columns>
              <asp:BoundColumn
                  DataField="title" HeaderText="タイトル" />
              <asp:BoundColumn
                  DataField="pubDate" HeaderText="日付" />
            </Columns>
          </asp:DataGrid>

        </ItemTemplate>
      </asp:TemplateColumn>
    </Columns>
  </asp:DataGrid>
</body>
</html>
DataSource属性を使用してDataGridコントロールを階層表示するC#のサンプル・プログラム(nesteddg2.aspx)

 DataGridコントロール内に記述したデータ連結式(<%# 〜 %>)においては、「Container」は行を示すDataGridItemオブジェクトである。よって、「Container.DataItem」はDataRowViewオブジェクトであり、最初のサンプル・プログラムと同じGetChildメソッドを使用して、DataSource属性に子データテーブルのデータビューを設定することができる。

 なお、DataBindメソッドによるデータ連結の実行は、そのコントロールの子コントロールにも波及する(コントロール・ツリーをたどってすべての子コントロールでデータ連結が実行される)。このため、こちらの方法では子DataGridコントロールでのDataBindメソッドの明示的な呼び出しは不要である。End of Article

カテゴリ:Webフォーム 処理対象:DataGridコントロール
使用ライブラリ:DataGridコントロール
使用ライブラリ:DataRelationクラス(System.Data名前空間)
関連TIPS:[ASP.NET]DataGridコントロールの行に通し番号を付けるには?
関連TIPS:
[ASP.NET]DataGridコントロールですべての行にアクセスするには?
 
この記事と関連性の高い別の.NET TIPS
[ASP.NET]DataGridコントロールでデータセットを表示するには?
[ASP.NET]Repeaterコントロールでネストされたデータを表現するには?
[ASP.NET]DataGridコントロールの編集用テキストボックスを大きくするには?
[ASP.NET]DataGridコントロールにソート機能を追加するには?
[ADO.NET]データテーブル(DataTable)内のレコードをソートするには?
このリストは、(株)デジタルアドバンテージが開発した
自動関連記事探索システム Jigsaw(ジグソー) により自動抽出したものです。
generated by

「.NET TIPS」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間