.NET TIPS

[ASP.NET]サイト共通のレイアウト部分を部品化するには?

山田 祥寛
2004/10/22

 開発生産性、あるいは、エンド・ユーザーのアクセシビリティといった観点から、テンプレートを用いてサイト・デザインの統一を図る手法は一般的だ。

 例えば、以下のようなサイトを想定してほしい。ヘッダ/メニュー部をサイト全体で統一し、コンテンツ部分だけをページごとに切り替えることで、開発者は本来の仕事内容であるコンテンツ作成に集中することができるし、サイト構成が一貫していることから、エンド・ユーザーも直感的にページを行き来できるようになる。また、サイト全体のデザインを変更したいという場合に、共通化されたヘッダ/メニュー部を一元的に更新できるというメリットもあるだろう。

ヘッダ/メニュー部をテンプレートにより統一したサイト
開発生産性やユーザビリティの観点から、テンプレートを用いてデザインの統一を図る手法は一般的に行われている。

 ASP.NET 1.1では厳密な意味では、サイト共通のデザインを「テンプレート」化する機能は用意されていないが、その代替機能として「ユーザー・コントロール」が用意されている。「ユーザー・コントロール」は、いうなれば「断片的なWebフォーム」とでも思っていただければよいだろう。実際、見掛けも仕組みもほとんど「.aspx」ファイルと変わらない。ユーザー・コントロールを用いることで、上述したようなサイト共通のレイアウト部分を容易に部品化することができるというわけだ。

 以下に具体的なユーザー・コントロールの例を見てみることにしよう。ここでは、あらかじめデータベース・サーバに用意されたmenuテーブルから簡単なナビゲート・バーを作成する。menuテーブルのレイアウトは、以下のとおり。

フィールド名 データ型 概要
title VARCHAR(50) タイトル
url VARCHAR(255) リンク先のURL(主キー)
indexNumber int 表示順
target VARCHAR(10) リンク時のターゲット(_blank、_selfなど)
menuテーブルのフィールド・レイアウト
 
<%@ Control Language="C#" %>
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.SqlClient" %>
<script runat="Server">
void Page_Load(Object sender, EventArgs e){
  SqlConnection db=new SqlConnection("Data Source=(local);User ID=sa;Password=sa;Persist Security Info=True;Initial Catalog=dotnet");
  SqlCommand objCom=new SqlCommand("SELECT url,title,target FROM menu ORDER BY indexNumber ASC",db);
  db.Open();
  SqlDataReader objDr=objCom.ExecuteReader();
  // menuテーブルから取得したリンク情報を順に出力
  while(objDr.Read()){
    // menuテーブルの情報を元にHyperLinkオブジェクトを生成
    HyperLink ctrl=new HyperLink();
    ctrl.NavigateUrl=objDr.GetString(0);
    ctrl.Text=objDr.GetString(1);
    ctrl.Target=objDr.GetString(2);
    // 区切り文字を表すLiteralControlオブジェクトを生成
    LiteralControl ctrl2=new LiteralControl();
    ctrl2.Text="&nbsp;&nbsp;|&nbsp;&nbsp;";
    // 動的に生成したコントロールを
    // あらかじめ配置したパネルに配置

    pnl.Controls.Add(ctrl);
    pnl.Controls.Add(ctrl2);
  }
}
</script>
<asp:Image id="img" runat="Server" Height="90" Width="150"
  ImageUrl="http://www.wings.msn.to/image/wings.jpg" AlternateText="Wings" />
<br />
<asp:Panel id="pnl" runat="Server"
  BorderStyle="Outset" BackColor="#FFffFF" ForeColor="#000000"
  Wrap="False" width="500" Height="20" />
menuテーブルから取得した情報を基にナビゲート・バーを生成するユーザー・コントロール(C#:header_cs.ascx)
 
<%@ Control Language="VB" %>
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.SqlClient" %>
<script runat="Server">
Sub Page_Load(sender As Object, e As EventArgs)
  Dim db As New SqlConnection("Data Source=(local);User ID=sa;Password=sa;Persist Security Info=True;Initial Catalog=dotnet")
  Dim objCom As New SqlCommand("SELECT url,title,target FROM menu ORDER BY indexNumber ASC",db)
  db.Open()
  Dim objDr As SqlDataReader=objCom.ExecuteReader()
  ' menuテーブルから取得したリンク情報を順に出力
  Do While objDr.Read()
    ' menuテーブルの情報を元にHyperLinkオブジェクトを生成
    Dim ctrl As New HyperLink()
    ctrl.NavigateUrl=objDr.GetString(0)
    ctrl.Text=objDr.GetString(1)
    ctrl.Target=objDr.GetString(2)
    ' 区切り文字を表すLiteralControlオブジェクトを生成
    Dim ctrl2 As New LiteralControl()
    ctrl2.Text="&nbsp;&nbsp;|&nbsp;&nbsp;"
    ' 動的に生成したコントロールを
    ' あらかじめ配置したパネルに配置

    pnl.Controls.Add(ctrl)
    pnl.Controls.Add(ctrl2)
  Loop
End Sub
</script>
<asp:Image id="img" runat="Server" Height="90" Width="150"
  ImageUrl="http://www.wings.msn.to/image/wings.jpg" AlternateText="Wings" />
<br />
<asp:Panel id="pnl" runat="Server"
  BorderStyle="Outset" BackColor="#FFffFF" ForeColor="#000000"
  Wrap="False" width="500" Height="20" />
menuテーブルから取得した情報を基にナビゲート・バーを生成するユーザー・コントロール(VB.NET:header_vb.ascx)

 上述したように、ユーザー・コントロールの記述はWebフォームに限りなく酷似しているが、構文的に異なるポイントがいくつかある。ロジックの詳細な流れについては、コード中のコメントを参照していただくとして、ここではユーザー・コントロール固有のポイントに焦点をあててみることにしよう。

(1)拡張子は「.ascx」

 Webフォームの拡張子が「.aspx」であるのに対し、ユーザー・コントロールの拡張子は「.ascx」でなければならない。ASP.NETは拡張子が「.ascx」であることをもって、そのファイルがユーザー・コントロールであることを認識するわけだ。

(2)@Pageディレクティブの代わりに、@Controlディレクティブを利用する

 .aspxファイルの処理方法を規定するのは、@Pageディレクティブの役割であった。この@Pageディレクティブに相当する機能を提供するのが、@Controlディレクティブである。@Controlディレクティブは、@Pageディレクティブのいわばサブセット的な位置付けであり、@Pageディレクティブと共通の属性を利用可能だ(@Pageディレクティブで利用可能な属性の詳細は、「プログラミングASP.NET―ASP.NETによるWebアプリケーション実践開発講座―」を参照)。

 ただし、@Pageディレクティブで定義された属性でもページ全体の処理を規定するようなものについては、@Controlディレクティブでは利用できないので注意が必要だ。以下には、@Pageディレクティブでは使えるが、@Controlディレクティブでは利用できない属性について一覧にまとめておく(逆に、@Pageディレクティブにはなくて、@Controlディレクティブでのみ使える属性はない)。

AutoCompat Buffer ClientTarget CodeBehind
CodePage ContentType Culture EnableSessionState
EnableViewStateMac ErrorPage LCID ResponseEncoding
SmartNavigation Trace TraceMode Transaction
UICulture ValidationRequest    
@Controlディレクティブでは利用できない@Pageディレクティブの属性

 なお、@Controlディレクティブは@Pageディレクティブ同様、.ascxファイル内に1回しか記述することができない。また、その性質上、(構文規則ではないが)一般的にはコードの先頭に記述するのが通例だ。

 以上のようにして作成したユーザー・コントロールを.aspxファイルで利用するには、以下のように記述すればよい。

<%@ Page Language="VB" ContentType="text/html" %>
<%@ Register Tagprefix="win" Tagname="NaviHeader" Src="header_cs.ascx" %>
<html>
<head>
<title>ユーザー・コントロール</title>
</head>
<body>
<form runat="Server">
  <win:NaviHeader id="navi" runat="Server" />
  ……以下にコンテンツが入ります……
</form>
</body>
</html>
ユーザー・コントロールheader_cs.aspxを利用する.aspxファイル(usrCtrl.aspx)

 ユーザー・コントロールを利用するには、作成した.ascxファイルを@Registerディレクティブにより.aspxファイルに登録するだけだ。@Registerディレクティブで指定する属性は以下のとおり。

属性名 概要
Tagprefix ユーザー・コントロールの接頭辞
Tagname ユーザー・コントロールを参照するためのタグ名
Src ユーザー・コントロールを定義した.ascxファイルへのパス
@Registerディレクティブで利用可能な属性

 これで、後は通常のサーバー・コントロールとまったく同じ要領でユーザー・コントロールを利用することができる。上のサンプル・コードでは、Src属性にheader_cs.ascxを指定しているが、header_vb.ascxを利用したい場合にはSrc属性だけを変更すればよい。

 ただし、ここで(構文規則ではないが)注意していただきたいのは、フォームである<form runat="Server">要素は原則的に.aspxファイルに記述するべきだということだ。構文的には.ascxファイルに記述しても間違いではないが、<form runat="Server">要素は1つの.aspxファイル内に1つしか記述することができない。このため、もしもユーザー・コントロールで<form runat="Server">要素を記述してしまった場合には、.aspxファイル内、または、ほかのユーザー・コントロールで<form runat="Server">要素の配下に記述するべきコントロールがあったとしてもそれらは使用できなくなってしまう。

 上記の.aspxファイル(usrCtrl.aspx)を実行すると、次の画面のようになる。先頭の画像と、その下にある横長のメニュー(ナビゲート・バー)が、ユーザー・コントロールにより表示されている部分だ。

ユーザー・コントロールを利用する.aspxファイルの実行結果

 なお、ASP.NETには、同様の仕組みとしてSSI(Server Side Include)の#IncludeディレクティブやHttpServerUtilityクラス(System.Web名前空間)のExecuteメソッドなども用意されているが、特別な理由がない限り、ユーザー・コントロールを利用するのが好ましい。

 というのも、#IncludeディレクティブはあくまでASP.NETとはまったく異なる仕組みであるため、(1)処理上のオーバーヘッドが大きい、(2)外部ファイルに対して任意の拡張子を付けられるため、例えば「.inc」のような拡張子を使用した場合には、ソース・コードが漏えいする危険性がある、などの問題がある。また、Executeメソッドにしても、やはり指定するのは.aspxファイルなので、(3)URLさえ分かってしまえば、外部ファイルに直接アクセスされてしまう恐れがある、などの問題は避けられない。

 その点、ユーザー・コントロールの.ascxファイルは、デフォルト設定で直接アクセスは認められていないし、フラグメント・キャッシュを利用することでパフォーマンスを向上することもできるなど、機能的にもセキュリティ的にもほかの2つの方法(#IncludeディレクティブとHttpServerUtilityクラスのExecuteメソッド)と比べて有利であるためだ。ユーザー・コントロールで利用可能なフラグメント・キャッシュについては、後日、「TIPS:[ASP.NET]Webフォーム・ページの一部分を断片的にキャッシングするには?」で紹介している。End of Article

カテゴリ:Webフォーム 処理対象:ユーザー・コントロール
使用キーワード:@Controlディレクティブ
使用キーワード:@Pageディレクティブ
使用キーワード:@Registerディレクティブ
使用キーワード:#Includeディレクティブ
使用ライブラリ:HttpServerUtilityクラス(System.Web名前空間)
関連TIPS:[ASP.NET]Webフォーム・ページの一部分を断片的にキャッシングするには?
 
この記事と関連性の高い別の.NET TIPS
[ASP.NET]Webフォーム・ページの一部分を断片的にキャッシングするには?
[ASP.NET]ユーザー・コントロールで属性を設定するには?
[ASP.NET]ページから生成されたソース・コードを見るには?
[ASP.NET]ユーザー・コントロールでパーソナライズ可能なWebパーツを作成するには?
[ASP.NET]アプリケーション共通の処理をPage派生クラスで実装するには?
このリストは、(株)デジタルアドバンテージが開発した
自動関連記事探索システム 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 記事ランキング

本日 月間