ASP_NET_MVC_4_Framework_s_primerami_na_C_dlya_p
.pdfТаблица 18-1: Свойства поиска движка Razor |
|
||
|
|
|
|
Свойство |
|
Описание |
Значение по умолчанию |
|
|
|
|
|
|
Адреса для |
~/Views/{1}/{0}.cshtml, |
ViewLocationFormats |
|
поиска |
|
|
представлений, |
~/Views/{1}/{0}.vbhtml, |
|
MasterLocationFormats |
|
частичных |
~/Views/Shared/{0}.cshtml, |
PartialViewLocationFormats |
|
||
|
|
представлений и |
~/Views/Shared/{0}.vbhtml |
|
|
макетов |
|
|
|
|
|
|
|
Адреса для |
|
|
|
поиска |
~/Areas/{2}/Views/{1}/{0}.cshtml, |
AreaViewLocationFormats |
|
представлений, |
|
|
~/Areas/{2}/Views/{1}/{0}.vbhtml, |
||
AreaMasterLocationFormats |
|
частичных |
|
AreaPartialViewLocationFormats |
|
представлений и |
~/Areas/{2}/Views/Shared/{0}.cshtml, |
|
|
макетов для |
~/Areas/{2}/Views/Shared/{0}.vbhtml |
|
|
областей |
|
Эти свойства предшествовали появлению Razor, поэтому каждой группе из трех свойств соответствует один набор значений. Каждый набор значений представляет собой массив строк. Ниже приведены значения параметров, которые соответствуют заполнителям:
{0} - имя представления.
{1} - имя контроллера.
{2} - имя области.
Для изменения адресов поиска создайте новый класс, наследующий от RazorViewEngine, и измените значения для одного или нескольких свойств, описанных в таблице 18-1.
Чтобы продемонстрировать, как изменять адреса поиска, мы добавили в приложение проект Infrastructure и создали движок представлений под названием CustomLocationViewEngine, который показан в листинге 18-12.
Листинг 18-12: Изменяем адреса поиска в Razor
using System.Web.Mvc;
namespace WorkingWithRazor.Infrastructure
{
public class CustomLocationViewEngine : RazorViewEngine
{
public CustomLocationViewEngine()
{
ViewLocationFormats = new string[] {"~/Views/{1}/{0}.cshtml", "~/Views/Common/{0}.cshtml"};
}
}
}
Мы установили новое значение для ViewLocationFormats. Наш новый массив содержит записи только для файлов .cshtml. Кроме того, мы изменили каталог для общих представлений на Views/Common вместо Views/Shared. Далее мы зарегистрировали наш пользовательский движок,
используя коллекцию ViewEngines.Engines в методе Application_Start файла Global.asax, как показано в листинге 18-13.
461
Листинг 18-13: Регистрируем пользовательский движок представлений
using System.Web.Http; using System.Web.Mvc;
using System.Web.Optimization; using System.Web.Routing;
using WorkingWithRazor.Infrastructure;
namespace WorkingWithRazor
{
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new CustomLocationViewEngine());
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
}
Вы должны помнить, что механизм вызова действий обращается к каждому движку по очереди, проверяя, какой из них может найти представление. Когда нам нужно было добавить в коллекцию пользовательский движок, она уже содержала стандартный движок Razor. Чтобы избежать конкуренции с этой реализацией, мы вызвали метод Clear и удалили все зарегистрированные движки, а затем зарегистрировали наш пользовательский движок с помощью метода Add.
Чтобы продемонстрировать изменение адресов поиска, мы создали папку /Views/Common и добавили в нее файл представления под названием List.cshtml. Содержимое этого файла показано в листинге
18-14.
Листинг 18-14: Содержимое файла /Views/Common/List.cshtml
@{
ViewBag.Title = "List";
}
<h3>This is the /Views/Common/List.cshtml View</h3>
Для отображения этого представления мы добавили в контроллер Home метод действия, показанный в листинге 18-15.
Листинг 18-15: Добавляем метод действия в контроллер Home
using System.Web.Mvc;
namespace WorkingWithRazor.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
string[] names = {"Apple", "Orange", "Pear"}; return View(names);
}
public ActionResult List()
{
462
Секции определяются с помощью тега Razor @, за которым следует имя секции. В нашем примере их две - Header и Footer. Их содержание представляет собой обычную смесь разметки HTML и тегов
Razor.
Чтобы указать, где в макете должны отображаться секции, используется вспомогательный метод @RenderSection. В листинге 18-17 показаны изменения, которые мы внесли в файл
~/Views/Shared/_Layout.cshtml.
Подсказка
Движок представлений все еще использует наши пользовательские адреса поиска, что означает, что макеты находятся в папке /Views/Shared, а представления - в
/Views/Common.
Листинг 18-17: Используем секции в макете
<!DOCTYPE html> <html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" /> <style type="text/css">
div.layout { background-color: lightgray;
}
div.view {
border: thin solid black; margin: 10px 0;
}
</style>
<title>@ViewBag.Title</title>
</head>
<body>
@RenderSection("Header")
<div class="layout">
This is part of the layout </div>
@RenderBody()
<div class="layout">
This is part of the layout </div>
@RenderSection("Footer")
<div class="layout">
This is part of the layout </div>
</body>
</html>
Когда Razor анализирует макет, он заменяет вспомогательный метод RenderSection на содержимое секции представления с указанным именем. Те части представления, которые не содержатся в секциях, вставляются в макет с помощью вспомогательного метода RenderBody.
465
Чтобы увидеть работу секций, запустите приложение, как показано на рисунке 18-6. Мы добавили некоторые базовые стили CSS, чтобы было понятно, какие из выведенных секций принадлежат представлению и какие - макету. Результат выглядит незамысловато, но он демонстрирует, как можно внедрить части контента представления в макет в заданной последовательности.
Рисунок 18-6: Используем секции в представлении, чтобы внедрить контент в макет
Примечание
В представлении можно определить только те секции, на которые есть ссылки в макете. При попытке определить в представлении секции, для которых нет соответствующего вызова вспомогательного метода @RenderSection в макете,
MVC Framework выбросит исключение.
Смешивание секций с остальным кодом представления – нестандартный подход. По соглашению секции определяются либо в начале, либо в конце представления, чтобы легче было увидеть, какие области контента будет рассматриваться как секции и будут захвачены вспомогательным методом RenderBody. Мы часто используем и другой подход, согласно которому представление должно содержать только секции, тело представления также заключается в секцию, как показано в листинге
18-18.
Листинг 18-18: Определяем представление с помощью секций Razor
@model string[] @{
ViewBag.Title = "Index";
}
@section Header { <div class="view">
@foreach (string str in new[] { "Home", "List", "Edit" })
{
@Html.ActionLink(str, str, null, new { style = "margin: 5px" })
}
466
</div>
}
@section Body { <div class="view">
This is a list of fruit names: @foreach (string name in Model)
{
<span><b>@name</b></span>
}
</div>
}
@section Footer { <div class="view">
This is the footer </div>
}
Мы считаем, что такой подход позволяет создавать более понятные представления и снижает вероятность того, что RenderBody захватит посторонний контент. Чтобы его применить, мы должны заменить вызов вспомогательного метода RenderBody на RenderSection("Body"), как показано в листинге 18-19.
Листинг 18-19: Определяем представление с помощью RenderSection("Body")
<!DOCTYPE html> <html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" /> <style type="text/css">
div.layout { background-color: lightgray;
}
div.view {
border: thin solid black; margin: 10px 0;
}
</style>
<title>@ViewBag.Title</title>
</head>
<body>
@RenderSection("Header")
<div class="layout">
This is part of the layout </div>
@RenderSection("Body")
<div class="layout">
This is part of the layout </div>
@RenderSection("Footer")
<div class="layout">
This is part of the layout </div>
</body>
</html>
467
Тестирование секций
Вы можете проверить, определена ли в представлении какая-либо секция из макета. Это делается для того, чтобы предоставить контент по умолчанию для этой секции, если она отсутствует в представлении и мы по каким-то причинам не хотим ее там определять. Мы изменили файл _Layout.cshtml, чтобы проверить, определена ли в нем секция Footer, как показано в листинге 1820.
Листинг 18-20: Проверяем наличие секции Footer в представлении
@if (IsSectionDefined("Footer"))
{
@RenderSection("Footer")
}
else
{
<h4>This is the default footer</h4>
}
Вспомогательный метод IsSectionDefined возвращает true для заданного имени секции, если в текущем представлении определена эта секция. В этом примере с помощью вспомогательного метода мы выясняем, нужно ли предоставлять какой-либо контент по умолчанию, если окажется, что в представлении не определена секция Footer.
Визуализируем дополнительные секции
По умолчанию в представлении должны быть определены все секции, к которым в макете имеются вызовы RenderSection. Если секции отсутствуют, MVC Framework покажет пользователю исключение. Чтобы продемонстрировать это, мы добавили в файл _Layout.cshtml новый вызов RenderSection к секции под названием scripts, как показано в листинге 18-21 (это секция, которую Visual Studio добавляет в макет по умолчанию для проектов на шаблоне Basic, но которую мы удалили в самом начале).
Листинг 18-21: Добавляем в макет вызов RenderSection, для которого нет соответствующей секции в представлении
<!DOCTYPE html> <html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" /> <style type="text/css">
div.layout { background-color: lightgray;
}
div.view {
border: thin solid black; margin: 10px 0;
}
</style>
<title>@ViewBag.Title</title>
</head>
<body>
@RenderSection("Header")
<div class="layout">
This is part of the layout </div>
468
@RenderSection("Body")
<div class="layout">
This is part of the layout </div>
@if (IsSectionDefined("Footer"))
{
@RenderSection("Footer")
}
else
{
<h4>This is the default footer</h4>
}
@RenderSection("scripts")
<div class="layout">
This is part of the layout </div>
</body>
</html>
Если мы запустим приложение и движок Razor попытается визуализировать макет и представление, то увидим ошибку, показанную на рисунке 18-7.
Рисунок 18-7: Ошибка, которая появляется в случае отсутствия секции
Вы можете использовать метод IsSectionDefined, чтобы избежать вызовов RenderSection к секциям, не определенным в представлении, но для этого есть более элегантный подход: передавать дополнительное значение false в метод RenderSection, как показано в листинге 18-22.
Листинг 18-22: Делаем секцию необязательной
@RenderSection("scripts", false)
469
Это делает секцию необязательной: если она определена в представлении, ее содержание будет вставлено в результат, если нет – мы не получим исключение.
Используем частичные представления
В приложениях мы часто используем одни и те же фрагменты тегов Razor и HTML-разметки в нескольких представлениях. Чтобы не дублировать контент, можно использовать частичные представления. Они представляют собой отдельные файлы, которые содержат фрагменты кода с тегами и разметкой и могут быть включены в другие представления. В этом разделе мы покажем вам, как создавать и использовать частичные представления, объясним принципы их работы и продемонстрируем способы передачи в них данных представлений.
Создаем частичное представление
Для начала создадим частичное представление под названием MyPartial. Для этого кликните правой кнопкой мыши по папке /Views/Shared, выберите из контекстного меню Add - View и отметьте флажком опцию Create as Partial View, как показано на рисунке 18-8.
Рисунок 18-8: Создаем частичное представление
470