.NET TIPS

[ASP.NET]TreeViewコントロールで深階層のツリー情報を効率よく読み込むには?[2.0のみ、C#、VB]

山田 祥寛
2006/06/23

 「TIPS:[ASP.NET]データベースからツリー・メニューを作成するには?」でも紹介したように、TreeViewコントロールを利用することで、データベース上で管理されたメニュー情報から動的にツリー形式のメニューを生成することが可能になる。

 しかし、前掲のTIPSで紹介した内容には、1つ問題がある。というのも、サイトの規模によっては、メニュー情報も(当然)膨大な分量になり、また階層も深くなる可能性がある。このようなサイトにおいて、初回起動時にすべてのツリー・ノードを展開しようとすると、処理にも時間がかかり、ひいてはアプリケーション全体のパフォーマンスを悪化させる原因になるだろう。

 そこで本稿では、TreeViewコントロール(System.Web.UI.WebControls名前空間)を展開するタイミングで、必要なノードのみを展開する方法を紹介する。本稿のテクニックを利用することで、深階層のツリー情報を表示する場合にも、より高いパフォーマンスを期待できる。

 それではさっそく、具体的な構築の手順をVisual Studio 2005(以降、VS 2005)環境を前提に見ていくことにしよう。なお、本稿のサンプル・プログラムを利用するには、前掲のTIPSに従って、データベース上にサイトマップ情報を格納するためのsitemapテーブルを作成しておく必要がある。

1. TreeViewコントロールを配置する

 まずは、フォーム・デザイナからTreeViewコントロールを配置してみよう。

 TreeViewコントロールを利用するのに必要な主な操作は、TreeViewコントロール右肩の[TreeView タスク]メニューを選択することで行えるが、ここでは[オートフォーマット]を選択し、ツリー・ビューの外観だけを整えてみよう。いくつかデフォルトのフォーマットが用意されているが、ここでは「Windowsヘルプ」を選択している。

[オートフォーマット]ダイアログ
本ダイアログは、[TreeView タスク]メニューから[オートフォーマット]を選択することで表示できる。ここでは「Windowsヘルプ」を選択している。

 また、以下の表の要領でTreeViewコントロールのプロパティ情報を設定しておこう。

プロパティ 設定値
(ID) tree
EnableClientScript False
ExpandDepth 0
TreeViewコントロールのプロパティ情報

 ExpandDepthプロパティは、TreeViewコントロールが最初に表示されたときに展開されるレベル数を表す。本稿のサンプル・プログラムでは、下位ノードの読み込みをノード展開の都度に行うので、ここではトップ・ノードのみを表示するように0と指定しておく。

 EnableClientScriptプロパティは、TreeNodeコントロール上のノードを展開/折りたたみする際に、クライアント・サイド・スクリプトで処理するかどうかを表す。本稿では、ノード展開の都度、サーバ・サイドでデータベースに対して下位ノードの情報を検索処理する必要があるので、False(無効)としておく。

 以上で、ツリー・メニューの外観にかかわる設定は完了だ。ここで参考までにVS 2005によって自動生成されたコードを引用しておく(ただし、<%--〜--%>は筆者コメント)。

<asp:TreeView ID="tree" runat="server"
  EnableClientScript="False" ExpandDepth="0"
  ImageSet="WindowsHelp">
  <%--ツリーの各ノードに関するスタイルの定義--%>
  <ParentNodeStyle Font-Bold="False" />
  <HoverNodeStyle Font-Underline="True" ForeColor="#6666AA" />
  <SelectedNodeStyle BackColor="#B5B5B5" Font-Underline="False"
    HorizontalPadding="0px" VerticalPadding="0px" />
  <NodeStyle Font-Names="Tahoma" Font-Size="8pt" ForeColor="Black"
    HorizontalPadding="5px" NodeSpacing="0px" VerticalPadding="1px" />
</asp:TreeView>
VS 2005により自動生成されたTreeViewコントロールの外観に関するソース・コード

2. ツリーにデータをバインドする

 もっとも、これだけでは単なる抽象的なツリーの定義にすぎないので、ページがロードされたタイミング、ノードを展開するタイミングで、それぞれデータベースから必要なメニュー情報を読み込み、TreeViewコントロールに展開する必要がある。

 以下に、その具体的なコードを見てみよう。なお、環境によって変動する可能性があるデータベース接続文字列を個々の.aspxファイルにハード・コーディングすることは好ましくない。ここではデータベース接続文字列は、構成ファイル(Web.config)上であらかじめ定義されているものとする(接続文字列の定義に関する詳細は、拙稿「無償データベース SQL Server 2005 Express Editionを使ってみよう」をご参照いただきたい)。

using System;
using System.Data;
using System.Data.Common;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;

public partial class TreeView_DbPopulate_cs : System.Web.UI.Page
{
  protected void Page_Load(object sender, EventArgs e)
  {
    // 初回ロード時に最上位のノードをTreeNodeコントロールに追加
    if (!Page.IsPostBack) {
      this.CreateNode("-", tree.Nodes);
    }
  }

  // 指定されたURL(parent)を親ノードとするノード群をツリーに追加
  private void CreateNode(String parent, TreeNodeCollection nodes)
  {
    ConnectionStringSettings setting =
      ConfigurationManager.ConnectionStrings["db"];
    DbProviderFactory factory =
      DbProviderFactories.GetFactory(setting.ProviderName);
    using (DbConnection db = factory.CreateConnection()) {
      db.ConnectionString = setting.ConnectionString;

      // パラメータparentをキーにsitemapテーブルを検索
      // (parentで指定されたURLを親に持つコンテンツを抽出)
      DbCommand comm= factory.CreateCommand();
      comm.CommandText =
        "SELECT url,title FROM sitemap WHERE parent=@parent";
      comm.Connection = db;
      DbParameter param= factory.CreateParameter();
      param.ParameterName = "@parent";
      param.Value = parent;
      comm.Parameters.Add(param);
      db.Open();
      DbDataReader reader = comm.ExecuteReader();

      // 取得したコンテンツを新規ノードとして現在ノードの配下に追加
      while (reader.Read()) {
        TreeNode node = new TreeNode();
        node.NavigateUrl = reader.GetString(0);
        node.Text = reader.GetString(1);
        node.PopulateOnDemand = true;
        node.Value = reader.GetString(0);
        nodes.Add(node);
      }
    }
  }

  // ノード展開時に、カレント・ノード配下に子ノードを追加
  protected void tree_TreeNodePopulate(Object sender, TreeNodeEventArgs e)
  {
    this.CreateNode(e.Node.Value, e.Node.ChildNodes);
  }
}
データベースから動的にツリー・メニューを生成するWebフォーム(C#:TreeView_DbPopulate_cs.aspx.cs)
 
Imports System.Data.Common

Partial Class TreeView_DbPopulate_vb
  Inherits System.Web.UI.Page

  Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load
    ' 初回ロード時に最上位のノードをTreeNodeコントロールに追加
    If Not Page.IsPostBack Then
      Me.CreateNode("-", tree.Nodes)
    End If
  End Sub

  ' 指定されたURL(parent)を親ノードとするノード群をツリーに追加
  Private Sub CreateNode(ByVal parent As String, ByVal nodes As TreeNodeCollection)
    Dim setting As ConnectionStringSettings = _
      ConfigurationManager.ConnectionStrings("db")
    Dim factory As DbProviderFactory = _
      DbProviderFactories.GetFactory(setting.ProviderName)
    Using db As DbConnection = factory.CreateConnection()
      db.ConnectionString = setting.ConnectionString
      ' パラメータparentをキーにsitemapテーブルを検索
      ' (parentで指定されたURLを親に持つコンテンツを抽出)
      Dim comm As DbCommand = factory.CreateCommand()
      comm.CommandText = _
        "SELECT url,title FROM sitemap WHERE parent=@parent"
      comm.Connection = db
      Dim param As DbParameter = factory.CreateParameter()
      param.ParameterName = "@parent"
      param.Value = parent
      comm.Parameters.Add(param)
      db.Open()
      Dim reader As DbDataReader = comm.ExecuteReader()

      ' 取得したコンテンツを新規ノードとして現在ノードの配下に追加
      Do While reader.Read()
        Dim node As New TreeNode()
        node.NavigateUrl = reader.GetString(0)  ' リンク先
        node.Text = reader.GetString(1)  ' ノード・テキスト
        node.Value = reader.GetString(0) ' ノード値
        node.PopulateOnDemand = True ' オンデマンドで子ノードを取得するか
        nodes.Add(node)
      Loop
    End Using
  End Sub

  ' ノード展開時に、カレント・ノード配下に子ノードを追加
  Protected Sub tree_TreeNodePopulate(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.TreeNodeEventArgs) Handles tree.TreeNodePopulate
    Me.CreateNode(e.Node.Value, e.Node.ChildNodes)
  End Sub
End Class
データベースから動的にツリー・メニューを生成するWebフォーム(VB:TreeView_DbPopulate_vb.aspx.vb)

 上記のコードでポイントとなるのは、下位ノードの読み込みをTreeNodePopulateイベントの発生したタイミングで行っているという点だ。TreeNodePopulateイベントは、ツリーの各ノードを展開したタイミングで発生する。本稿のサンプル・プログラムでは、TreeNodePopulateイベントに対応するイベント・ハンドラはtree_TreeNodePopulateメソッドだ。

 tree_TreeNodePopulateメソッドでは、第2パラメータに引き渡されるTreeNodeEventArgsオブジェクト(System.Web.UI.WebControls名前空間)のNodeプロパティにより、展開対象のノードがTreeNodeオブジェクト(System.Web.UI.WebControls名前空間)として返される。

 CreateNodeメソッドは、取得したTreeNodeオブジェクトに属する子ノード群をデータベースから取得し、現在のツリーに追加する。基本的なロジックは、前掲の「TIPS:[ASP.NET]データベースからツリー・メニューを作成するには?」とほぼ同等であるので、詳細はそちらを参照していただきたい。

 本稿で注目していただきたいのは、追加ノード(TreeNodeオブジェクト)のPopulateOnDemandプロパティにTrueを指定しているという点だ。このプロパティをTrueに設定することで動的ノード・ポピュレーション機能を有効にし、実行時にノード・データを設定することが可能になる。ノード・データの設定には、前述のTreeNodePopulateイベント・ハンドラを使用すればよい。また、ポストバック処理時のキー値としてValueプロパティに、カレント・ノードのURLをセットしている点にも注目してほしい。

 以上が理解できたら、さっそく、サンプル・コードを実行してみよう。

サンプル・プログラムの実行結果

 データベースに格納した内容に従って、このようなツリーが表示されれば成功だ。ノード展開のたびにポストバックが発生し、下位ノードの情報が読み込まれていることが確認できるはずだ。End of Article

利用可能バージョン:.NET Framework 2.0のみ
カテゴリ:Webフォーム 処理対象:ナビゲーション
使用ライブラリ:TreeViewコントロール(System.Web.UI.WebControls名前空間)
使用ライブラリ:TreeNodeEventArgsクラス(System.Web.UI.WebControls名前空間)
使用ライブラリ:TreeNodeクラス(System.Web.UI.WebControls名前空間)
関連TIPS:[ASP.NET]データベースからツリー・メニューを作成するには?

この記事と関連性の高い別の.NET TIPS
[ASP.NET]TreeViewコントロールで深階層のツリー情報を効率よく読み込むには?
[ASP.NET]データベースからツリー・メニューを作成するには?
[ASP.NET]データベースからツリー・メニューを生成するには?
TreeViewコントロールで現在選択されているノードを変更するには?
TreeViewコントロールへ項目を追加するには?
TreeViewコントロールで効率的にツリーを構築するには?
[ASP.NET]データベースからリッチなメニューを作成するには?
[ASP.NET]TreeViewコントロールでツリー・メニューを作成するには?
このリストは、(株)デジタルアドバンテージが開発した
自動関連記事探索システム 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 記事ランキング

本日 月間