Поделиться через


Часть 3. Представления и классы ViewModel

Джон Галлоуэй

Музыкальное хранилище MVC — это учебное приложение, которое представляет и объясняет пошаговые инструкции по использованию ASP.NET MVC и Visual Studio для веб-разработки.

Музыкальный магазин MVC — это упрощенный пример реализации магазина, который продает музыкальные альбомы в Интернете и реализует базовые функции администрирования сайтов, входа пользователей и корзины для покупок.

В этой серии учебников подробно описаны все шаги, предпринятые для создания примера приложения ASP.NET MVC Music Store. Часть 3 охватывает представления и модели представления.

До сих пор мы только что возвращали строки из действий контроллера. Это хороший способ получить представление о том, как работают контроллеры, но это не то, как вы хотите создать реальное веб-приложение. Нам нужен лучший способ создания HTML-кода обратно в браузеры, посещающие наш сайт, где мы можем использовать файлы шаблонов, чтобы упростить настройку HTML-содержимого, отправляемого обратно. Это именно то, что делают Представления.

Добавление шаблона представления

Чтобы использовать шаблон представления, мы изменим метод HomeController Index так, чтобы он возвращал ActionResult, и возвращал View(), как показано ниже:

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

Приведенное выше изменение указывает, что вместо возвращаемой строки мы хотим использовать "Представление" для создания результата обратно.

Теперь мы добавим в проект соответствующий шаблон Представления. Для этого мы разместим текстовый курсор в методе действия Индекс, а затем щелкните правой кнопкой мыши и выберите "Добавить представление". Откроется диалоговое окно Добавление представления:

Снимок экрана: меню, в котором отображается выбор представления добавления.Снимок экрана: диалоговое окно добавления представления с параметрами меню для выбора и добавления представления.

Диалоговое окно "Добавление представления" позволяет быстро и легко создавать файлы шаблонов представления. По умолчанию диалоговое окно "Добавление представления" предварительно заполняет имя создаваемого шаблона представления, чтобы оно соответствовало методу действия, который будет его использовать. Так как мы использовали контекстное меню "Добавить представление" в методе действия Index() нашего HomeController, диалоговое окно "Добавление представления" выше содержит "Индекс" в качестве имени представления, которое предварительно заполнено по умолчанию. Нам не нужно изменять параметры в этом диалоговом окне, поэтому нажмите кнопку Добавить.

При нажатии кнопки Добавить Visual Web Developer создаст для нас новый шаблон представления Index.cshtml в каталоге \Views\Home, создав папку, если она еще не существует.

Снимок экрана: раскрывающееся меню Обозреватель решений с различными файлами в музыкальном магазине M V C.

Имя и расположение папки файла Index.cshtml важны и соответствуют стандартным ASP.NET соглашениям об именовании MVC. Имя каталога \Views\Home соответствует контроллеру с именем HomeController. Имя шаблона представления Index соответствует методу действия контроллера, который будет отображать представление.

ASP.NET MVC позволяет избежать необходимости явно указывать имя или расположение шаблона представления при использовании этого соглашения об именовании для возврата представления. По умолчанию шаблон представления \Views\Home\Index.cshtml будет отображаться при написании кода, как показано ниже в homeController:

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. Подсистема представления веб-формы по-прежнему доступна в ASP.NET MVC 3, но многие разработчики считают, что подсистема представления Razor хорошо подходит ASP.NET разработке MVC.

Первые три строки задают заголовок страницы с помощью ViewBag.Title. В ближайшее время мы рассмотрим, как это работает, но сначала обновим текст заголовка и просмотрите страницу. <Обновите тег h2>, чтобы он сказал "Это домашняя страница", как показано ниже.

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

Запуск приложения показывает, что наш новый текст отображается на домашней странице.

Снимок экрана: домашняя страница браузера музыкального магазина с текстом

Использование макета для общих элементов сайта

Большинство веб-сайтов имеют содержимое, которое совместно используется многими страницами: навигация, нижние колонтитулы, изображения логотипов, ссылки на таблицы стилей и т. д. Подсистема представлений Razor упрощает управление с помощью страницы с именем _Layout.cshtml, которая автоматически создается в папке /Views/Shared.

Снимок экрана: раскрывающееся меню

Дважды щелкните эту папку, чтобы просмотреть содержимое, которое показано ниже.

<!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-файл и изображения включены в каталог Содержимое MvcMusicStore-Assets.zip который доступен в MVC-Music-Store. Мы выберем их в Windows Обозреватель и добавим их в папку Содержимое решения в Visual Web Developer, как показано ниже:

Параллельный снимок экрана: каталог содержимого и раскрывающееся меню

Вам будет предложено подтвердить, хотите ли вы перезаписать существующий файл Site.css. Нажмите кнопку "Да".

Снимок экрана: появилось всплывающее окно с предупреждением с запросом на подтверждение действия перезаписи путем запроса на замену существующего файла.

Теперь папка Содержимое приложения будет выглядеть следующим образом:

Снимок экрана: хранилище музыки, раскрывающееся меню, выделенная папка содержимого, в которой отображается новая папка изображений со списком изображений под ней.

Теперь давайте запустим приложение и посмотрим, как выглядят наши изменения на домашней странице.

Снимок экрана: домашняя страница окна браузера музыкального магазина с выбранным изображением и словами

  • Давайте рассмотрим, что изменилось: метод действия Индекс HomeController нашел и отобразил шаблон \Views\Home\Index.cshtmlView, хотя наш код называется "return View()", так как шаблон View соответствует стандартному соглашению об именовании.
  • На домашней странице отображается простое приветственное сообщение, определенное в шаблоне представления \Views\Home\Index.cshtml.
  • Домашняя страница использует шаблон _Layout.cshtml, поэтому приветственное сообщение содержится в стандартном макете HTML сайта.

Использование модели для передачи информации в представление

Шаблон Представления, который просто отображает жестко закодированный HTML, не сделает веб-сайт очень интересным. Чтобы создать динамический веб-сайт, мы хотим передать сведения из действий контроллера в шаблоны представлений.

В шаблоне model-view-controller термин Модель относится к объектам, которые представляют данные в приложении. Часто объекты модели соответствуют таблицам в базе данных, но это не обязательно.

Методы действий контроллера, возвращающие ActionResult, могут передавать объект модели в представление. Это позволяет контроллеру правильно упаковывать все сведения, необходимые для создания ответа, а затем передавать эти сведения в шаблон Представления для создания соответствующего HTML-ответа. Это проще всего понять, увидев его в действии, поэтому давайте приступим к работе.

Сначала мы создадим классы моделей для представления жанров и альбомов в нашем магазине. Начнем с создания класса Genre. Щелкните правой кнопкой мыши папку Models в проекте, выберите параметр Add Class (Добавить класс) и присвойте файлу имя Genre.cs.

Снимок экрана: три параллельных поля меню, показывающие направления пути к файлу справа налево к выбору класса

Снимок экрана: параметры меню добавления нового элемента с тремя меню, в котором выбран шаблон, стиль сортировки и тип; затем строка поля имени в нижней части.

Затем добавьте свойство public string Name в созданный класс:

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

Примечание. Если вам интересно, нотация { get; set; } использует функцию автоматически реализуемых свойств C#. Это дает нам преимущества свойства без необходимости объявлять резервное поле.

Затем выполните те же действия, чтобы создать класс Album (с именем Album.cs), имеющий заголовок и свойство Genre:

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

Теперь можно изменить StoreController для использования представлений, которые отображают динамическую информацию из модели. Если в целях демонстрации мы назвали альбомы на основе идентификатора запроса, мы могли бы отобразить эти сведения, как в представлении ниже.

Снимок экрана: домашняя страница в браузере с логотипом изображения, текущим названием альбома и кнопками

Начнем с изменения действия "Сведения о магазине", чтобы в нем отображались сведения для одного альбома. Добавьте оператор using в начало класса StoreControllers , чтобы включить пространство имен 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;

Далее мы обновим действие контроллера Сведений, чтобы оно возвращалось ActionResult, а не строку, как это было с методом Index HomeController.

public ActionResult Details(int id)

Теперь можно изменить логику, чтобы вернуть объект Album в представление. Далее в этом руководстве мы будем извлекать данные из базы данных, но сейчас мы будем использовать фиктивные данные для начала работы.

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

Примечание. Если вы не знакомы с C#, можно предположить, что использование var означает, что наша переменная альбома привязана с опозданием. Неправильно. Компилятор C# использует вывод типа на основе того, что мы присваиваем переменной, чтобы определить, что альбом относится к типу Альбом, и компилирует локальную переменную альбома как тип альбома, поэтому мы получаем поддержку во время компиляции и редактор кода Visual Studio.

Теперь создадим шаблон Представление, который использует наш альбом для создания HTML-ответа. Прежде чем это сделать, необходимо выполнить сборку проекта, чтобы в диалоговом окне Добавить представление было известно о созданном классе Album. Вы можете выполнить сборку проекта, выбрав пункт меню Отладка⇨Сборка MvcMusicStore (для получения дополнительного кредита можно использовать сочетание клавиш CTRL+SHIFT+B для сборки проекта).

Снимок экрана: редактор документов музыкального магазина с выбранной вкладкой

Теперь, когда мы настроили вспомогательные классы, мы готовы создать шаблон Представления. Щелкните правой кнопкой мыши метод Details и выберите "Добавить представление..." из контекстного меню.

Снимок экрана: меню шаблона представления, отображающее фрагмент кода и выделение параметра

Мы создадим новый шаблон Представления, как и раньше с помощью HomeController. Так как мы создаем его из StoreController, он по умолчанию создается в файле \Views\Store\Index.cshtml.

В отличие от ранее, мы будем проверка флажок "Создать строго типизированное" представление. Затем мы выберем класс "Альбом" в раскрывающемся списке "Просмотреть класс данных". В результате диалоговое окно "Добавление представления" создаст шаблон Представления, который ожидает, что объект Album будет передан ему для использования.

Снимок экрана: окно меню

При нажатии кнопки "Добавить" будет создан шаблон представления \Views\Store\Details.cshtml, содержащий следующий код.

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

Обратите внимание на первую строку, которая указывает, что это представление строго типизировано для нашего класса Album. Подсистема представлений Razor понимает, что он был передан объект Album, поэтому мы можем легко получить доступ к свойствам модели и даже воспользоваться преимуществами IntelliSense в редакторе Visual Web Developer.

<Обновите тег h2>, чтобы он отображал свойство Название альбома, изменив строку следующим образом.

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

Обратите внимание, что IntelliSense активируется при вводе периода после @Model ключевое слово с отображением свойств и методов, поддерживаемых классом Album.

Теперь давайте повторно запустим проект и перейдем по URL-адресу /Store/Details/5. Мы увидим подробные сведения об альбоме, как показано ниже.

Снимок экрана: окно браузера домашней страницы с изображением логотипа в левом верхнем углу и именем альбома под ним.

Теперь мы создадим аналогичное обновление для метода действия Обзор в Магазине. Обновите метод таким образом, чтобы он возвращал ActionResult, и измените логику метода, чтобы он создал новый объект Genre и вернул его в представление.

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

Щелкните правой кнопкой мыши метод Browse и выберите "Добавить представление..." в контекстном меню, а затем добавьте строго типизированный вид в класс Genre.

Снимок экрана: контекстное меню, в котором выбрано

<Обновите элемент h2> в коде представления (в /Views/Store/Browse.cshtml), чтобы отобразить сведения о жанре.

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

Теперь давайте повторно запустите наш проект и перейдите к /Store/Browse? Genre=Disco URL. Страница Обзор отобразится, как показано ниже.

Снимок экрана: окно домашней страницы браузера с сообщением

Наконец, давайте немного усложним обновление метода действия Индекса Магазина и представления, чтобы отобразить список всех жанров в нашем магазине. Мы будем делать это, используя список жанров в качестве объекта модели, а не просто один жанр.

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

Щелкните правой кнопкой мыши метод действия Store Index и выберите Добавить представление, как и раньше, выберите Тип в качестве класса Модель и нажмите кнопку Добавить.

Снимок экрана: меню окна

Сначала мы изменим @model объявление, чтобы указать, что представление будет ожидать несколько объектов 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 типа Genre.

Снимок экрана: фрагмент кода H T M L с строкой меню, на которой выбрана команда count <>.

В цикле foreach визуальный веб-разработчик знает, что каждый элемент имеет тип "Жанр", поэтому мы видим IntelliSense для каждого типа "Жанр".

Снимок экрана: код

Затем функция формирования шаблонов изучила объект Genre и определила, что каждый из них будет иметь свойство Name, поэтому он циклически просматривает и записывает их. Он также создает ссылки "Изменить", "Сведения" и "Удалить" для каждого отдельного элемента. Мы воспользуемся этим позже в нашем менеджере магазина, но сейчас мы хотели бы иметь простой список.

Когда мы запускаем приложение и переходим в /Store, мы видим, что отображается и количество, и список жанров.

Снимок экрана: окно браузера с заголовком

В нашем URL-адресе /Store, в списке жанров в настоящее время имена жанров отображаются просто как обычный текст. Давайте изменим это так, чтобы вместо обычного текста у нас была ссылка на названия жанров на соответствующий URL-адрес /Store/Browse, чтобы при щелчке по жанру музыки, например "Disco", переходил по URL-адресу /Store/Browse?genre=Disco. Мы можем обновить шаблон представления \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, доступные в нашем коде шаблона View для выполнения различных стандартных задач. Вспомогательный метод Html.ActionLink() особенно полезен и позволяет легко создавать ссылки в ФОРМАТЕ HTML <> и отвечает за досадные сведения, такие как обеспечение правильной кодировки URL-путей.

Html.ActionLink() имеет несколько разных перегрузок, позволяющих указать столько сведений, сколько необходимо для ссылок. В простейшем случае вы укажете только текст ссылки и метод Action для перехода при щелчке гиперссылки на клиенте. Например, мы можем связать метод "/Store/" Index() на странице Сведений о Магазине с текстом ссылки "Перейти к индексу Магазина", используя следующий вызов:

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

Примечание. В этом случае нам не нужно указывать имя контроллера, так как мы просто связывали другое действие в том же контроллере, который отображает текущее представление.

Однако наши ссылки на страницу Обзор должны передать параметр, поэтому мы будем использовать другую перегрузку метода Html.ActionLink, которая принимает три параметра:

    1. Текст ссылки, в котором будет отображаться имя жанра
    1. Имя действия контроллера (Обзор)
    1. Значения параметров маршрута с указанием имени (Жанр) и значения (Имя жанра)

Объединив все это вместе, мы напишем эти ссылки в представление Индекса Магазина:

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

Теперь, когда мы снова запустим проект и перейдем к URL-адресу /Store/, мы увидим список жанров. Каждый жанр является гиперссылкой. Щелкнув его, мы перейдем по URL-адресу /Store/Browse?genre=[genre] .

Снимок экрана: окно браузера с заголовком

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>