第 3 部:Views と ViewModels

Jon Galloway

MVC ミュージック ストアは、web 開発に MVC と Visual Studio ASP.NET 使用する手順を紹介し、説明するチュートリアル アプリケーションです。

MVC ミュージック ストアは、音楽アルバムをオンラインで販売し、基本的なサイト管理、ユーザー サインイン、ショッピング カート機能を実装する軽量のサンプル ストア実装です。

このチュートリアル シリーズでは、ASP.NET MVC Music Store サンプル アプリケーションをビルドするために実行されるすべての手順について詳しく説明します。 パート 3 では、ビューと ViewModel について説明します。

ここまでは、コントローラー アクションから文字列を返しました。 これはコントローラーのしくみを把握するのに便利な方法ですが、実際の Web アプリケーションを構築する方法ではありません。 私たちは、私たちのサイトにアクセスしているブラウザにHTMLを生成するより良い方法を望んでいます- テンプレートファイルを使用してHTMLコンテンツの送信をより簡単にカスタマイズすることができます。 ビューの機能はまさにそのものです。

ビュー テンプレートの追加

ビュー テンプレートを使用するには、次のように、HomeController Index メソッドを変更して ActionResult を返し、View() を返します。

public class HomeController : Controller
{
    //
    // GET: /Home/
    public ActionResult Index()
    {
        return View();
    }
}

上記の変更は、文字列を返す代わりに、"View" を使用して結果を生成することを示しています。

次に、プロジェクトに適切なビュー テンプレートを追加します。 これを行うには、Index アクション メソッド内にテキスト カーソルを配置し、右クリックして [ビューの追加] を選択します。 これにより、[ビューの追加] ダイアログが表示されます。

ビューの追加の選択を示すメニューのスクリーンショット。ビューの追加ダイアログ ボックスのスクリーンショット。ビューを選択して追加するためのメニュー オプションがあります。

[ビューの追加] ダイアログでは、ビュー テンプレート ファイルをすばやく簡単に生成できます。 既定では、[ビューの追加] ダイアログでは、作成するビュー テンプレートの名前が事前に設定され、それを使用するアクション メソッドと一致します。 HomeController の Index() アクション メソッド内で "ビューの追加" コンテキスト メニューを使用したため、上記の [ビューの追加] ダイアログには、既定で事前設定されたビュー名として "インデックス" があります。 このダイアログのオプションを変更する必要はないので、[追加] ボタンをクリックします。

[追加] ボタンをクリックすると、Visual Web Developer によって新しい Index.cshtml ビュー テンプレートが \Views\Home ディレクトリに作成され、フォルダーがまだ存在しない場合は作成されます。

[ソリューション エクスプローラー] ドロップダウン メニューのスクリーンショット。M V C ミュージック ストアのさまざまなファイルが表示されています。

"Index.cshtml" ファイルの名前とフォルダーの場所は重要であり、MVC の名前付け規則 ASP.NET 既定に従います。 ディレクトリ名 \Views\Home は、コントローラー (HomeController という名前) と一致します。 ビュー テンプレート名 Index は、ビューを表示するコントローラー アクション メソッドと一致します。

ASP.NET MVC では、この名前付け規則を使用してビューを返すときに、ビュー テンプレートの名前または場所を明示的に指定する必要がなくなります。 HomeController 内で次のようなコードを記述すると、既定で \Views\Home\Index.cshtml ビュー テンプレートがレンダリングされます。

public class HomeController : Controller
{
    //
    // GET: /Home/
    public ActionResult Index()
    {
        return View();
    }
}

Visual Web Developer では、[ビューの追加] ダイアログで [追加] ボタンをクリックした後、"Index.cshtml" ビュー テンプレートを作成して開きました。 Index.cshtml の内容を次に示します。

@{
    ViewBag.Title = "Index";
}
<h2>Index</h2>

このビューでは Razor 構文を使用しています。これは、ASP.NET Web Formsおよび以前のバージョンの ASP.NET MVC で使用されるWeb Forms ビュー エンジンよりも簡潔です。 Web Forms ビュー エンジンは、ASP.NET MVC 3 で引き続き使用できますが、多くの開発者は、Razor ビュー エンジンが MVC 開発 ASP.NET 適していることを確認しています。

最初の 3 行では、ViewBag.Title を使用してページ タイトルを設定します。 このしくみについては、すぐに詳しく説明しますが、まずテキスト見出しのテキストを更新し、ページを表示しましょう。 次に示すように、 <h2> タグを "This is the Home Page" と更新します。

@{
    ViewBag.Title = "Index";
}
<h2>This is the Home Page</h2>

アプリケーションを実行すると、ホーム ページに新しいテキストが表示されます。

ミュージック ストアのブラウザーのホーム ページのスクリーンショット。ロゴ画像の下に

一般的なサイト要素にレイアウトを使用する

ほとんどの Web サイトには、ナビゲーション、フッター、ロゴ画像、スタイルシート参照など、多くのページ間で共有されるコンテンツがあります。Razor ビュー エンジンを使用すると、/Views/Shared フォルダー内に自動的に作成された _Layout.cshtml というページを使用して、これを簡単に管理できます。

[ミュージック ストア] ドロップダウン ファイル メニューのスクリーンショット。ビュー フォルダー内にある共有フォルダーへのファイル パスが示されています。

次に示す内容を表示するには、このフォルダーをダブルクリックします。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>@ViewBag.Title</title>
    <link href="@Url.Content("~/Content/Site.css")"
rel="stylesheet" type="text/css" />
    <script src="@Url.Content("~/Scripts/jquery-1.5.1.min.js")"     
            type="text/javascript"></script> 
    <script src="@Url.Content("~/Scripts/modernizr-1.7.min.js")"
            type="text/javascript"></script>
</head>
<body>
    @RenderBody()
</body>
</html>

個々のビューのコンテンツはコマンドによって @RenderBody() 表示され、その外部に表示する一般的なコンテンツは、_Layout.cshtml マークアップに追加できます。 MVC ミュージック ストアには、サイト内のすべてのページのホーム ページとストア領域へのリンクを含む共通ヘッダーを用意する必要があるため、そのステートメントのすぐ上のテンプレートに @RenderBody() 追加します。

<!DOCTYPE html>
<html>
<head>
    <title>@ViewBag.Title</title>
    <link href="@Url.Content("~/Content/Site.css")"
rel="stylesheet" type="text/css" />
    <script src="@Url.Content("~/Scripts/jquery-1.4.4.min.js")"
type="text/javascript"></script>
</head>
<body>
    <div id="header">
        <h1>
            ASP.NET MVC MUSIC STORE</h1>
        <ul id="navlist">
            <li class="first"><a href="/"
id="current">Home</a></li>
            <li><a
href="/Store/">Store</a></li>
        </ul>
    </div>
    @RenderBody()
</body>
</html>

スタイルシートの更新

空のプロジェクト テンプレートには、検証メッセージを表示するために使用されるスタイルのみを含む、非常に合理化された CSS ファイルが含まれています。 デザイナーは、サイトの外観を定義するために追加の CSS と画像をいくつか提供したので、それらを今すぐ追加します。

更新された CSS ファイルとイメージは、 MVC-Music-Store で入手できるMvcMusicStore-Assets.zipの Content ディレクトリに含まれています。 次に示すように、Windows エクスプローラーで両方を選択し、Visual Web Developer のソリューションのコンテンツ フォルダーにドロップします。

コンテンツ ディレクトリと [ミュージック ストア] ドロップダウン メニューのサイド バイ サイドスクリーンショット。コンテンツ フォルダー内の images フォルダーへのファイル パスが示されています。

既存の Site.css ファイルを上書きするかどうかを確認するメッセージが表示されます。 [はい] をクリックします。

既存のファイルを置き換えるかどうかを確認して上書き操作の確認を求める警告ポップアップ ボックスが表示されるスクリーンショット。

アプリケーションの Content フォルダーが次のように表示されます。

ミュージック ストアのドロップダウン メニューのスクリーンショット。コンテンツ フォルダーが強調表示され、下に画像の一覧が表示された新しい画像フォルダーが表示されています。

次に、アプリケーションを実行し、ホーム ページで変更がどのように表示されるかを確認しましょう。

ミュージック ストア ブラウザー ウィンドウのホーム ページのスクリーンショット。下に [this is the home page]\(これがホーム ページです\) という単語と共に選択された画像が表示されています。

  • 変更された内容を確認してみましょう。HomeController の Index アクション メソッドは、"return View()" というコードが標準の名前付け規則に従っているため、"return View()" と呼ばれるコードであっても、\Views\Home\Index.cshtmlView テンプレートを見つけて表示しました。
  • ホーム ページには、\Views\Home\Index.cshtml ビュー テンプレート内で定義された単純なウェルカム メッセージが表示されます。
  • ホーム ページは _Layout.cshtml テンプレートを使用しているため、ウェルカム メッセージは標準サイトの HTML レイアウトに含まれています。

モデルを使用してビューに情報を渡す

ハードコーディングされた HTML だけを表示するビュー テンプレートは、非常に興味深い Web サイトになることはありません。 動的 Web サイトを作成するには、代わりにコントローラー アクションからビュー テンプレートに情報を渡します。

Model-View-Controller パターンでは、Model という用語は、アプリケーション内のデータを表すオブジェクトを指します。 多くの場合、モデル オブジェクトはデータベース内のテーブルに対応していますが、必要はありません。

ActionResult を返すコントローラー アクション メソッドは、モデル オブジェクトをビューに渡すことができます。 これにより、コントローラーは応答の生成に必要なすべての情報をクリーンにパッケージ化し、この情報をビュー テンプレートに渡して、適切な HTML 応答を生成できます。 これは実際の動作を確認して理解するのが最も簡単なので、始めましょう。

まず、ストア内のジャンルとアルバムを表すモデル クラスをいくつか作成します。 最初に Genre クラスを作成してみましょう。 プロジェクト内の [Models] フォルダーを右クリックし、[クラスの追加] オプションを選択し、ファイルに "Genre.cs" という名前を付けます。

右から左へのファイル パスの方向からクラスの選択までを示す 3 つのサイド バイ サイド メニュー ボックスのスクリーンショット

テンプレート、並べ替えスタイル、および種類を選択する 3 つのメニューが表示されている、新しい項目の追加メニュー オプションのスクリーンショット。次に、下部にある名前フィールド バーを選択します。

次に、作成されたクラスにパブリック文字列 Name プロパティを追加します。

public class Genre
{
    public string Name { get; set; }
}

注: 疑問に思う場合は、{get; set; } 表記が C# の自動実装プロパティ機能を使用しています。 これにより、バッキング フィールドを宣言しなくても、プロパティの利点が得られます。

次に、同じ手順に従って、Title プロパティと Genre プロパティを持つ Album クラス (Album.cs という名前) を作成します。

public class Album
{
    public string Title { get; set; }
    public Genre Genre { get; set; }
}

これで、モデルの動的な情報を表示するビューを使用するように StoreController を変更できるようになりました。 デモンストレーションの目的で、要求 ID に基づいてアルバムに名前を付けた場合は、次のビューのようにその情報を表示できます。

ブラウザーのホーム ページのスクリーンショット。画像ロゴ、現在のアルバム名、クリック可能なホームボタンとストア ボタンが右上隅にあります。

まず、ストアの詳細アクションを変更して、1 つのアルバムの情報が表示されるようにします。 StoreControllers クラスの先頭に "using" ステートメントを追加して MvcMusicStore.Models 名前空間を含めます。そのため、アルバム クラスを使用するたびに MvcMusicStore.Models.Album を入力する必要はありません。 これで、そのクラスの "usings" セクションが次のように表示されます。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MvcMusicStore.Models;

次に、詳細コントローラー アクションを更新して、HomeController の Index メソッドと同様に、文字列ではなく ActionResult を返します。

public ActionResult Details(int id)

これで、ビューに Album オブジェクトを返すようにロジックを変更できます。 このチュートリアルの後半では、データベースからデータを取得しますが、現時点では "ダミー データ" を使用して作業を開始します。

public ActionResult Details(int id)
 {
    var album = new Album { Title = "Album " + id };
    return View(album);
 }

注: C# に慣れていない場合は、var を使用すると、アルバム変数が遅延バインドされることを意味すると考えられます。 正解ではありません。C# コンパイラは、変数に割り当てられている内容に基づいて型推論を使用して、アルバムの種類が Album であると判断し、ローカル アルバム変数をアルバム型としてコンパイルしているため、コンパイル時のチェックと Visual Studio コード エディターのサポートが得られます。

次に、アルバムを使用して HTML 応答を生成するビュー テンプレートを作成しましょう。 これを行う前に、新しく作成した Album クラスについて [ビューの追加] ダイアログで認識できるようにプロジェクトをビルドする必要があります。 [デバッグ]⇨[Build MvcMusicStore]\(MvcMusicStore のビルド\) メニュー項目を選択してプロジェクトをビルドできます (追加のクレジットの場合は、Ctrl + Shift + B ショートカットを使用してプロジェクトをビルドできます)。

[build]\(ビルド\) タブがドロップダウン メニューで選択されているミュージック ストア ドキュメント エディターのスクリーンショット。[build M V C music store]\(ビルド M V C ミュージック ストア\) オプションが強調表示されています。

サポート クラスを設定したので、View テンプレートをビルドする準備ができました。 Details メソッド内で右クリックし、[ビューの追加]を選択します。をクリックします。

[ビュー テンプレート] メニューのスクリーンショット。コード スニペットの上に表示され、[ビューの追加] オプションが強調表示されています。

前に HomeController で行ったように、新しいビュー テンプレートを作成します。 StoreController から作成するため、既定では \Views\Store\Index.cshtml ファイルに生成されます。

以前とは異なり、[厳密に型指定されたビューを作成する] チェック ボックスをオンにします。 次に、[View data-class]\(データ クラスの表示\) ドロップダウン リスト内で "Album" クラスを選択します。 これにより、[ビューの追加] ダイアログで、使用する Album オブジェクトが渡されることを想定したビュー テンプレートが作成されます。

[ビューの追加] メニュー ウィンドウのスクリーンショット。クリック可能な [厳密に型指定されたビューの作成] チェック ボックスとアルバムのモデル クラスが表示されています。

[追加] ボタンをクリックすると、次のコードを含む \Views\Store\Details.cshtml ビュー テンプレートが作成されます。

@model MvcMusicStore.Models.Album
@{
    ViewBag.Title = "Details";
}
<h2>Details</h2>

このビューがアルバム クラスに厳密に型指定されていることを示す最初の行に注目してください。 Razor ビュー エンジンは、アルバム オブジェクトが渡されたことを認識しているため、モデルのプロパティに簡単にアクセスでき、Visual Web Developer エディターで IntelliSense の利点を得ることもできます。

h2> タグを<更新して、次のように行を変更して、アルバムの Title プロパティを表示します。

<h2>Album: @Model.Title</h2>

キーワードの後 @Model にピリオドを入力すると IntelliSense がトリガーされ、Album クラスがサポートするプロパティとメソッドが表示されます。

プロジェクトを再実行し、/Store/Details/5 URL にアクセスしてみましょう。 以下のようなアルバムの詳細が表示されます。

左上に画像ロゴが表示され、その下にアルバム名が表示されているホーム ページ ブラウザー ウィンドウのスクリーンショット。

次に、ストア参照アクション メソッドに対して同様の更新を行います。 ActionResult を返すようにメソッドを更新し、新しい Genre オブジェクトを作成して View に返すようにメソッド ロジックを変更します。

public ActionResult Browse(string genre)
 {
    var genreModel = new Genre { Name = genre };
    return View(genreModel);
 }

Browse メソッドを右クリックし、[ビューの追加]を選択します。コンテキスト メニューから、厳密に型指定された View を追加し、厳密に型指定された View を Genre クラスに追加します。

[厳密に型指定されたビューの作成] が選択され、現在のモデル クラスが赤でボックス化されていることを示すコンテキスト メニューのスクリーンショット。

<ビュー コード (/Views/Store/Browse.cshtml) の h2> 要素を更新して、ジャンル情報を表示します。

@model MvcMusicStore.Models.Genre
@{
    ViewBag.Title = "Browse";
}
<h2>Browsing Genre: @Model.Name</h2>

次に、プロジェクトを再実行し、/Store/Browse を参照してみましょう。Genre=Disco URL。 [参照] ページが次のように表示されます。

ブラウザーのホーム ページ ウィンドウのスクリーンショット。ロゴ画像の下に

最後に、 Store Index アクション メソッドを少し複雑に更新し、ストア内のすべてのジャンルの一覧を表示します。 これを行うには、単一のジャンルではなく、モデル オブジェクトとしてジャンルのリストを使用します。

public ActionResult Index()
{
    var genres = new List<Genre>
    {
        new Genre { Name = "Disco"},
        new Genre { Name = "Jazz"},
        new Genre { Name = "Rock"}
    };
    return View(genres);
 }

[ストア インデックス] アクション メソッドを右クリックし、前と同じように [ビューの追加] を選択し、Model クラスとして [ジャンル] を選択して、[追加] ボタンを押します。

[ビューの追加] ウィンドウ メニューのスクリーンショット。赤いボックス内にモデル クラスの選択が表示され、次に下の [追加] ボタンが表示されています。

最初に、宣言を @model 変更して、ビューが 1 つだけではなく複数の Genre オブジェクトを必要とすることを示します。 /Store/Index.cshtml の最初の行を次のように変更します。

@model IEnumerable<MvcMusicStore.Models.Genre>

これにより、Razor ビュー エンジンは、複数の Genre オブジェクトを保持できるモデル オブジェクトを操作することを通知します。 リスト ジャンルではなく>< IEnumerable<ジャンル>を使用しています。これはより汎用的であるため、後で IEnumerable インターフェイスをサポートする任意のオブジェクト型にモデルの種類を変更できます。

次に、以下の完成したビュー コードに示すように、モデル内の Genre オブジェクトをループ処理します。

@model IEnumerable<MvcMusicStore.Models.Genre>
@{
    ViewBag.Title = "Store";
}
<h3>Browse Genres</h3>
<p>
    Select from @Model.Count()
genres:</p>
<ul>
    @foreach (var genre in Model)
    {
        <li>@genre.Name</li>
    }
</ul>

このコードを入力する際に IntelliSense が完全にサポートされていることに注意してください。"@Model" と入力すると、ジャンル型の IEnumerable でサポートされているすべてのメソッドとプロパティが表示されます。

H T M L コード スニペットのスクリーンショット。メニュー バーが表示され、'count <>' コマンドが選択されています。

"foreach" ループ内では、Visual Web Developer は各項目がジャンル型であることを認識しているため、ジャンルの種類ごとに IntelliSense が表示されます。

'foreach ループ' コードのスクリーンショット。ドロップダウン メニュー ウィンドウと 、その横に 'string Genre dot name' がポップアップ表示された [名前] オプションが選択されています。

次に、スキャフォールディング機能によって Genre オブジェクトが調べられ、それぞれが Name プロパティを持つことになるので、ループして書き出します。また、個々のアイテムへの編集、詳細、および削除リンクも生成されます。 後でストア マネージャーでこれを利用しますが、ここでは単純なリストを作成したいと考えます。

アプリケーションを実行して /Store を参照すると、ジャンルの数と一覧の両方が表示されます。

ブラウザー ウィンドウのスクリーンショット。

現在、ジャンルを一覧表示する /Store URL には、単にプレーン テキストとしてジャンル名が一覧表示されています。 これを変更して、プレーンテキストではなく、適切な /Store/Browse URL へのジャンル名のリンクを作成し、"Disco" のような音楽ジャンルをクリックすると 、/Store/Browse?genre=Disco URL に移動するようにします。 \Views\Store\Index.cshtml ビュー テンプレートを更新し、次のようなコードを使用してこれらのリンクを出力できます (これを入力しないでください。改善します)。

<ul>
    @foreach (var genre in Model)
    {
        <li><a href="/Store/Browse?genre=@genre.Name">@genre.Name</a></li>
    }
</ul>

これは機能しますが、ハードコーディングされた文字列に依存するため、後で問題が発生する可能性があります。 たとえば、コントローラーの名前を変更する場合は、更新する必要があるリンクを探してコードを検索する必要があります。

使用できる別の方法は、HTML ヘルパー メソッドを利用することです。 ASP.NET MVC には、このようなさまざまな一般的なタスクを実行するために、ビュー テンプレート コードから使用できる HTML ヘルパー メソッドが含まれています。 Html.ActionLink() ヘルパー メソッドは特に便利なメソッドであり、HTML <> のリンクを簡単に作成し、URL パスが正しく URL エンコードされていることを確認するなど、面倒な詳細を処理します。

Html.ActionLink() には、リンクに必要な量の情報を指定できるようにするために、いくつかの異なるオーバーロードがあります。 最も簡単なケースでは、リンク テキストと、ハイパーリンクがクライアントでクリックされたときに移動する Action メソッドのみを指定します。 たとえば、次の呼び出しを使用して、ストアの詳細ページの "/Store/" Index() メソッドにリンクし、リンク テキスト "Go to the Store Index" を使用できます。

@Html.ActionLink("Go
to the Store Index", "Index")

注: この場合、現在のビューをレンダリングしている同じコントローラー内の別のアクションにリンクしているだけなので、コントローラー名を指定する必要はありませんでした。

ただし、[参照] ページへのリンクではパラメーターを渡す必要があるため、次の 3 つのパラメーターを受け取る Html.ActionLink メソッドの別のオーバーロードを使用します。

    1. ジャンル名を表示するリンク テキスト
    1. コントローラー アクション名 (参照)
    1. 名前 (ジャンル) と値 (ジャンル名) の両方を指定するルート パラメーター値

これらすべてをまとめると、これらのリンクをストア インデックス ビューに書き込む方法を次に示します。

<ul>
    @foreach (var genre in Model)
    {
        <li>@Html.ActionLink(genre.Name,
"Browse", new { genre = genre.Name })</li>
    }
</ul>

プロジェクトをもう一度実行し、/Store/ URL にアクセスすると、ジャンルの一覧が表示されます。 各ジャンルはハイパーリンクです。クリックすると、/Store/Browse?genre=[genre] URL に移動します。

ブラウザー ウィンドウのスクリーンショット。[ジャンルの参照] タイトルが表示され、メッセージが 3 つのジャンルから選択され、続いて 3 つの箇条書きのジャンルが選択されています。

ジャンル リストの HTML は次のようになります。

<ul>
    <li><a href="/Store/Browse?genre=Disco">Disco</a>
</li>
    <li><a href="/Store/Browse?genre=Jazz">Jazz</a>
</li>
    <li><a href="/Store/Browse?genre=Rock">Rock</a>
</li>
</ul>