суббота, 17 мая 2008 г.

MVC. Представления

А теперь о том, что видит пользователь, и что видит программист, создающий страницу.
В MVC можно использовать обычные веб-страницы с обычными веб-контролами, но это (лично мне) уже совершенно неинтересно. Технология MVC очаровала меня своей "неподкупной простотой", после которой очень трудно вернуться в прошлое тяжеловесных веб-контролов.

Вообще обычные веб-страницы MVC, т.е. представления являются наследниками класса ViewPagе, а тот, в свою очередь, наследует обычный Page и, кроме того, реализует интерфейс IViewDataContainer. По умолчанию экземпляр ViewPage не включает тэг <form>, поэтому, если вы захотите добавить серверные контролы, не забудьте добавить и форму.
Следует отметить, что представление должно служить только для отображения данных, оно не должно содержать никакой логики или кода доступа к данным. Просто взять данные и отобразить их.

Передача данных
Главное, ради чего создаются веб-страницы (и вообще приложение) - это данные, которые должны быть переданы форме (в нашем случае представлению). В MVC существует несколько способов передать странице данные. Рассмотрим их по порядку.
1. Использование словаря ViewData
Класс Controller содержит член ViewData, он объявлен следующим образом:

public IDictionary<string, object> ViewData { get; }

Как видим, это простая дженерик коллекция, доступ к ней получаем из самого контроллера:

ViewData["CategoryName"] = Category.Name;
ViewData["Categories"] = categories; //categories - это <List>Category

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

<h2><% = ViewData["CategoryName"] %></h2>

Для отображения повторяющихся частей страницы (как в Repeater) можно использовать цикл foreach:

<select>
<%foreach (var cat in ((<List>)ViewData["Categories"]) ){ %>
<option value='<%=cat.id %>'> <%= cat.Name %> </option>
</select>

Обращаю ваше внимание на две вещи:
1. Несмотря на то, что cat имеет "неопределенный" тип var, мы можем обращаться к полям его настоящего типа, поскольку к нему мы привели объект ViewData["Categories"].
2. Мы использовали встроенный код как для содержимого контейнера (для option), так и для определения атрибута тэга (value) - во втором случае кавычки обязательны (даже несмотря на то, что в обычных тэгах мы можем их и не ставить).

2. Использование типизированных объектов
Вы можете создавать слой DAL самостоятельно, я пытаюсь использовать MS EntityFramework, в любом случае у нас есть доступ к типизированным объектам и коллекциям, которые мы прекрасно можем передать странице. Делается это с помощью перегруженного метода RenderView, который в качестве второго параметра принимает любой объект:

protected void RenderView(string viewName, object viewData);

Вот туда-то мы и можем передать, например, список категорий:

RenderView("Category", categories); //categories - это <List>Category

На страницу этот параметр приходит в виде ViewData, поэтому далее мы работаем с ним также, как было показано выше.

Но есть еще более эффективный способ использовать типизированные объекты на странице (уже без всякого приведения). Дело в том, что в MS MVC существует еще один класс, наследующий ViewPage, определенный следующим образом:

public class ViewPage<TViewData> : System.Web.Mvc.ViewPage
Member of System.Web.Mvc

TViewData - это шаблон, вместо него вы подставляете имя класса, содержащего необходимые данные.
Все, что надо сделать - это наследовать представление от ViewPage<TViewData>, например:

public partial class Index : ViewPage<Category>
{
}

После чего прямо на странице можно легко обращаться к ViewData, объекту, который в нашем примере имеет тип Category:

<%=ViewData.Subcategory %>

Может показаться, что данный подход не очень удобен для сложных страниц, которые отображают разные наборы данных, однако не составляет труда сделать дополнительные классы, которые будут иметь в своем составе все необходимые коллекции, и обращаться к ним через ViewData.

Ссылки по теме:
ScottGu о контроллерах (и представлениях)
ASP.NET Model View Controller Applications > Views and Rendering Helpers

Комментариев нет: