ASP.NET MVC 4
Адам Фриман
Настройка системы шаблонных вспомогательных методов
Мы научились с помощью метаданных влиять на то, как шаблонные вспомогательные методы отображают элементы. Однако, как и для всех функций MVC Framework, для шаблонных вспомогательных методов есть дополнительные опции, которые позволяют полностью настроить их работу. В следующих разделах мы продемонстрируем, как можно дополнить или заменить встроенную поддержку для реализации конкретных решений.
Создаем пользовательский шаблон Editor
Самый простой способ изменить поведение шаблонного вспомогательного метода – создать пользовательский шаблон. Это позволит нам сгенерировать для свойства модели именно такой HTML, какой мы хотим.
Чтобы продемонстрировать, как работает этот подход, мы создадим пользовательский шаблон для свойства Role
класса Person
. Тип этого свойства - значение из перечисления Role
, и нам не нравится то, как оно визуализируется. Шаблонный вспомогательный метод создает обычный элемент input
, который позволяет пользователю ввести любое значение, не ограничивая их теми, которые указаны в перечислении.
MVC Framework будет искать пользовательские шаблоны для элементов editor
в папке /Views/Shared/EditorTemplates
, поэтому мы добавили эту папку в проект и создали в ней новое строго типизированное частичное представление под названием Role.cshtml
. Вы можете увидеть содержимое этого файла в листинге 20-19.
Листинг 20-19: Содержимое файла Role.cshtml
@model HelperMethods.Models.Role
@Html.DropDownListFor(m => m,
new SelectList(Enum.GetNames(Model.GetType()),
Model.ToString()))
Тип модели для данного представления – это перечисление Role
, и мы используем вспомогательный метод Html.DropDownListFor
, чтобы создать элемент select
с элементами option
для значений в перечислении. Мы передаем дополнительное значение в конструктор SelectList
; оно указывает выбранное значение, которое мы получаем от объект модели представления. Метод DropDownListFor
и объект SelectList
работают со строковыми значениями, поэтому мы должны преобразовать значения в перечислении и модели представления.
Когда мы будем использовать шаблонный вспомогательный метод, чтобы создать элемент editor
для типа Role
, будет использоваться файл /Views/Shared/EditorTemplates/Role.cshtml
. Таким образом, мы получаем последовательное и полезное представление для типа данных. Эффект применения пользовательского шаблона показан на рисунке 20-11.
Рисунок 20-11: Эффект применения пользовательского шаблона для перечисления Role
Порядок поиска шаблонов
Шаблон
Role.cshtml
работает потому, что платформа MVC сначала выполняет поиск пользовательских шаблонов для данного типа C#, и только потом использует один из встроенных шаблонов. Поиск подходящего шаблона MVC Framework проводит в следующем порядке:
- Шаблон, переданный во вспомогательный метод - например, вспомогательный метод
Html.EditorFor(m => m.SomeProperty, "MyTemplate")
нашел бы шаблонMyTemplate
.- Шаблон, который указан в атрибутах метаданных, таких как
UIHint
.- Шаблон, который связан с типом данных, указанным в метаданных, например в атрибуте
DataType
.- Шаблон, который соответствует имени класса .NET обрабатываемого типа данных.
- Встроенный шаблон
String
, если обрабатываемый тип данных является простым.- Шаблон, который соответствует базовому классу типа данных.
- Если тип данных реализует
IEnumerable
, то будет использоваться встроенный шаблонCollection
. Если ничего не подходит, то будет использоваться шаблонObject
, на который распространяется правило, что формирование шаблонов не является рекурсивным.В некоторых пунктах используются встроенные шаблоны, которые описаны в таблице 20-4. На каждом этапе процесса поиска шаблона MVC Framework ищет шаблон под названием
EditorTemplates/<name>
для вспомогательных методов для элементовeditor
иDisplayTemplates/<name>
для вспомогательных методов для элементовdisplay
. Для нашего шаблонаRole
поиск остановится на пункте 4; мы создали шаблон под названиемRole.cshtml
и поместили его в папку/Views/Shared/EditorTemplates
.Поиск пользовательских шаблонов проводится по той же схеме, что и поиск обычных представлений, а следовательно, мы можем создать пользовательский шаблон для конкретного контроллера и поместить его в папку
~/Views/<controller>/EditorTemplates
, чтобы переопределить шаблоны из папки~/Views/Shared
. Подробная информация о поиске представлений дана в главе 18.
Создаем общий (generic) шаблон
Можно создавать шаблоны не только для конкретного типа. Мы можем, например, создать шаблон для всех перечислений, а затем выбрать его с помощью атрибута UIHint
. Если вы посмотрите блок «Порядок поиска шаблонов», то увидите, что шаблоны, заданные с помощью атрибута UIHint
, имеют приоритет над шаблонами для конкретного типа.
Чтобы продемонстрировать, как это работает, мы создали новое представление под названием Enum.cshtml
в папке /Views/Shared/EditorTemplates
. Содержимое этого файла показано в листинге 20-20.
Листинг 20-20: Представление Enum.cshtml
@model Enum
@Html.DropDownListFor(m => m, Enum.GetValues(Model.GetType())
.Cast<enum>()
.Select(m =>
{
string enumVal = Enum.GetName(Model.GetType(), m);
return new SelectListItem()
{
Selected = (Model.ToString() == enumVal),
Text = enumVal,
Value = enumVal
};
})
Типом модели для этого шаблона является Enum
, что позволяет нам работать с любым перечислением. Для разнообразия мы создали с помощью LINQ строки, которые необходимы для создания элементов select
и option
. Далее мы можем затем применить атрибут UIHint
. В нашем проекте есть дополняющий класс метаданных, так что мы применили этот атрибут к классу PersonMetadata
, как показано в листинге 20-21. (Напомним, что этот класс находится по адресу /Models/Metadata/PersonMetadata.cs
).
Листинг 20-21: Используем атрибут UIHint
, чтобы указать пользовательский шаблон
using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
namespace HelperMethods.Models
{
[DisplayName("New Person")]
public partial class PersonMetaData
{
[HiddenInput(DisplayValue = false)]
public int PersonId { get; set; }
[Display(Name = "First")]
public string FirstName { get; set; }
[Display(Name = "Last")]
public string LastName { get; set; }
[Display(Name = "Birth Date")]
[DataType(DataType.Date)]
public DateTime BirthDate { get; set; }
[Display(Name = "Approved")]
public bool IsApproved { get; set; }
[UIHint("Enum")]
public Role Role { get; set; }
}
}
Такой подход позволяет получить более общее решение, которое можно применять во всем приложении и гарантировать, что все свойства Enum
отображаются с помощью элемента select
. Мы предпочитаем создавать пользовательские шаблоны для конкретных типов моделей, но иногда удобнее иметь шаблон, который можно применять более широко.
Заменяем встроенные шаблоны
Если мы создадим пользовательский шаблон с таким же именем, как и один из встроенных шаблонов, MVC Framework будет использовать пользовательскую версию в предпочтение встроенной. В листинге 20-22 показано содержимое файла Boolean.cshtml
, который мы создали в папке /Views/Shared/EditorTemplates
. Это представление заменяет встроенный шаблон Boolean
, который используется для отображения значений bool
и bool?
.
Листинг 20-22: Заменяем встроенный шаблон
@model bool?
@if (ViewData.ModelMetadata.IsNullableValueType && Model == null) {
@:(True) (False) <b>(Not Set)</b>
} else if (Model.Value) {
@:<b>(True)</b> (False) (Not Set)
} else {
@:(True) <b>(False)</b> (Not Set)
}
В данном представлении мы отображаем все возможные значения и выделяем то, которое соответствует объекту модели. Результат применения этого шаблона показан на рисунке 20-12.
Рисунок 20-12: Результат переопределения встроенного шаблона для элементов editor
Обратите внимание на гибкость пользовательских шаблонов, хотя в этом примере мы используем их не самым полезным образом и даже не позволяем пользователю изменять значение свойства. Как видите, существует много способов контролировать, как должны отображаться и редактироваться свойства модели, и вы можете выбрать подход, который лучше всего соответствует вашему стилю программирования и конкретному приложению.