連載:ASP.NET MVC入門【バージョン3対応】

第7回 レイアウト/部分ビューでアプリ共通のデザインを定義

山田 祥寛(http://www.wings.msn.to/
2011/09/22
Page1 Page2 Page3 Page4

部分ビュー

 部分ビューとは、名前のとおり、部分的(断片的)なビュー・テンプレートのこと。レイアウトにも似ているが、レイアウトがヘッダやフッタのようなページの外枠を定義することを目的としているのに対して、部分ビューはもう少し断片的な――デザインの共通領域を定義することを目的としている。Webフォーム(ASPXエンジン)でいうところの、ユーザー・コントロールに相当するものと考えればよいだろう。

■部分ビューの基本

 それではさっそく、部分ビューを具体的に利用してみよう。以下のサンプルは、第2回で作成した書籍詳細画面を部分ビューとして部品化したものだ。

[1]部分ビューを作成する

 部分ビューを新規に作成するには、ソリューション・エクスプローラから「~/Views/Shared」フォルダを右クリックし、コンテキスト・メニューから[追加]−[ビュー]を選択する。

 Sharedフォルダを選択しているのは、部分ビューを複数のコントローラで共有することを想定しているためだ。特定のコントローラでのみ利用するような部分ビューは、「~Views/コントローラ名」フォルダに配置しても構わない。


図5 [ビューの追加]ダイアログ

 [ビューの追加]ダイアログが表示されるので、表1の要領で必要な情報を入力する。

項目 設定値(例)
ビュー名 _BookDetails
ビュー エンジン Razor
厳密に型指定されたビューを作成する チェックする
スキャフォールディング テンプレート Empty
部分ビューとして作成する チェック
表1 [ビューの追加]ダイアログの設定

 部分ビューを作成する場合には、[部分ビューとして作成する]のチェックは必須だ。また、[ビュー名]はアンダースコア(_)で始まる名前を指定すること。Razorエンジンでは、これによって、そのテンプレートが部分ビューであると認識するためだ。

 正確には、「Razorは、名前がアンダースコアで始まるビューへの直接アクセスを禁止」している。通常、部分ビューへの直接アクセスは想定していないはずなので、あらかじめ名前付けレベルでガードしておくのが望ましい。

[参考].cshtml/.vbhtmlの直接呼び出しは禁止がデフォルト

 もっと厳密にいうと、ASP.NET MVCは、そもそも.cshtml/.vbhtmlファイルへの直接アクセスを禁止している。例えば、以下は、

http://localhost:21742/Views/Home/Index.cshtml

のように、テンプレート・ファイルに直接アクセスした場合の結果だ。


図6 テンプレート・ファイルに直接アクセスした場合

 「404 Not Foundエラー」で、テンプレート・ファイルが存在すらしていないように見える。しかし、「~/Views/Web.config」(「~/Web.config」ではない)の設定を変更することで、この挙動を変更することも可能だ(変更部分は太字で表している)。

<appSettings>
  <add key="webpages:Enabled" value="true" />
</appSettings
リスト12 ビュー・テンプレート.cshtml/.vbhtmlの直接アクセスを許可する設定(Web.config)

 webpages:Enabledパラメータは、.cshtml/.vbhtmlファイルを単体で動作するWebページとして有効にするかどうかを決めるものだ。この状態で、もう一度、Home/Index.cshtmlにアクセスしてみると、確かに結果が変化する。


図7 テンプレート・ファイルに直接アクセスした場合(設定変更後)

 直接アクセスを想定したテンプレートでないため、エラーにはなっているが、今度はテンプレートが認識された状態になっていることが確認できる。もちろん、ASP.NET MVCアプリケーションでは、テンプレートへの直接アクセスを許すべきではないので、この設定を変更してはならない。

 ということで、部分ビューに対して「_」で始まる名前を付けなくとも、最低限、部分ビューへの直接アクセスは防げる。しかし、部分ビューであることを視覚的に判別しやすくするという意味でも、まずは標準的な命名ルールに沿っておくのが望ましい。

 [追加]ボタンをクリックすると、_BookDetails.cshtml/_BookDetails.vbhtmlファイルには@model/@ModelTypeディレクティブだけが生成されているはずなので、スキャフォールディング機能で生成済みのDetails.cshtml/Details.vbhtmlファイルから、必要なコードをコピー&ペーストしてほしい(該当箇所は太字で表している)。

@model MvcTemplate.Models.Book

<fieldset>
  <legend>Book</legend>

  <div class="display-label">Title</div>
  <div class="display-field">
    @Html.DisplayFor(model => model.Title)
  </div>

  <div class="display-label">Price</div>
  <div class="display-field">
    @Html.DisplayFor(model => model.Price)
  </div>
  ……中略……
</fieldset>

@ModelType MvcTemplateVb.Book

<fieldset>
  <legend>Book</legend>

  <div class="display-label">Title</div>
  <div class="display-field">
    @Html.DisplayFor(Function(model) model.Title)
  </div>

  <div class="display-label">Price</div>
  <div class="display-field">
    @Html.DisplayFor(Function(model) model.Price)
  </div>
  ……中略……
</fieldset>

リスト13 詳細画面を表す部分ビューのコード(上:_BookDetails.cshtml、下:_BookDetail.vbhtml)

 コードを見ても分かるように、部分ビューとはいっても、何ら特別な構文があるわけではない。メイン・テンプレートの一部を切り出せば、それがそのまま部分ビューとなるわけだ。

[3]部分ビューを引用する

 あとは、この部分ビューをメイン・テンプレートから呼び出すだけだ(リスト14)。部分ビューで記述されている部分は、ばっさりと削除してしまって構わない。

<h2>Details</h2>

@Html.Partial("_BookDetails", Model)

<p>
リスト14 部分ビューを引用するためのコード(Details.cshtml/Details.vbhtml)

 部分ビューを呼び出すには、ビュー・ヘルパーの一種であるHtml.Partialメソッドを呼び出すだけだ。Partialメソッドの構文は、以下のとおり。

Partial(string viewName [,object model])
リスト15 Html.Partialメソッドの構文
viewName:部分ビューのベース名
model:モデルを表すオブジェクト

 Partialメソッドは、以下の順序で部分ビューを検索する

  1. ~/Views/コントローラ名/ビュー名.aspx
  2. ~/Views/コントローラ名/ビュー名.ascx
  3. ~/Views/Shared/ビュー名.aspx
  4. ~/Views/Shared/ビュー名.ascx
  5. ~/Views/コントローラ名/ビュー名.cshtml
  6. ~/Views/コントローラ名/ビュー名.vbhtml
  7. ~/Views/Shared/ビュー名.cshtml
  8. ~/Views/Shared/ビュー名.vbhtml

 つまり、同名の部分ビューがあった場合には、Shareフォルダのそれよりも現在のコントローラに属する部分ビューが優先されるし、(まずないと思うが)同名の.aspx/.ascxファイルがあった場合にはそちらが優先されるので注意されたい。

 なお、メイン・テンプレートのモデルをそのまま引き継ぐのであれば、引数modelは省略可能だ。よって、リスト14の太字部分は「@Html.Partial("_BookDetails")」としても構わない。

[参考]ASPXエンジンは無効化しておこう

 ASP.NET MVC 3はデフォルトで、WebFormViewEngine(ASPX)/RazorViewEngineエンジンを有効にしている。しかし、アプリケーションでRazorしか利用していないならば、ASPXエンジンは除去しておくのが望ましいだろう。

 これによって、ASPXテンプレート検索のオーバーヘッドがなくなるので、わずかながらパフォーマンスが改善する(この事情は、部分ビュー検索の場合だけでなく、通常のビュー、レイアウトでも同じだ)。

 ASPXエンジンを除去するには、Global.asaxのApplication_Startイベント・ハンドラーに以下のようなコードを記述すればよい。

protected void Application_Start() {
  ViewEngines.Engines.Clear();
  ViewEngines.Engines.Add(new RazorViewEngine());
  ……中略……
}
Sub Application_Start()
  ViewEngines.Engines.Clear()
  ViewEngines.Engines.Add(New RazorViewEngine())
  ……中略……
End Sub
リスト16 ASPXビュー・エンジンを無効化するコード(上:C#、下:VB)

 Partialメソッドの代わりに、RenderPartialメソッドを利用することも可能だ。両者はほとんど同じ役割を提供するが、戻り値の型が異なるので要注意だ。

 Partialメソッドが文字列(MvcHtmlString型)で部分ビューの出力を返すのに対して、RenderPartialメソッドは応答ストリームに直接出力する(メソッドの戻り値の型はvoid)。よって、リスト14の太字部分と同じ意味のことをRenderPartialメソッドで表すならば、以下のようにコード・ブロックの中に記述しなければならない。

@{ Html.RenderPartial("_BookDetails", Model); }
@Code
  Html.RenderPartial("_BookDetails", Model)
End Code
リスト17 RenderPartialメソッドで部分ビューを呼び出すコード(上:C#、下:VB)

 「@Html.RenderPartial("_BookDetail", Model)」のようには書けないので注意されたい。記述のシンプルさを考えれば、まずはPartialメソッドを優先して利用することをお勧めしたい*4

*4 ただし、「Razor: Html.RenderPartial vs Html.Partial, Html.RenderAction vs Html.Action ? what one should use?のような意見もある。


 INDEX
  ASP.NET MVC入門【バージョン3対応】
  第7回 レイアウト/部分ビューでアプリ共通のデザインを定義
    1.ヘッダ/フッタ/メニューを共通化する − レイアウト
    2.レイアウトに複数のコンテンツ領域を設置/レイアウトを入れ子に配置
  3.部分ビューの基本
    4.アクションを伴う部分ビュー呼び出し/子アクションでフラグメント・キャッシュ
 
インデックス・ページヘ  「ASP.NET MVC入門【バージョン3対応】」


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 記事ランキング

本日 月間