No último post eu mostrei como customizar a renderização de um campo DateTime do seu Model através do MVC Futures (para saber mais, clique aqui). Neste post vou mostrar como utilizar o mesmo mecanismo para renderizar tipos complexos.

Modelo

No modelo abaixo demonstrado temos duas classes bastante simples: ListaPresenca e Aluno, onde a classe ListaPresenca possui uma coleção de objetos do tipo Aluno:

MVC Futures

Por convenção, ao utilizar o helper Html.EditorFor, o ASP.NET MVC procura dentro da pasta Views\Shared\EditorTemplates algum arquivo .ascx que tenha o mesmo nome do tipo de dado que está sendo renderizado. Por exemplo, quando utilizamos o comando Html.EditorFor(m => m.NomeInstrutor), o framework vai procurar o template String.ascx e utilizá-lo para renderizar a propriedade NomeInstrutor, pois ela é do tipo String. Caso não exista nenhum .ascx com o nome do tipo a ser renderizado, o framework utiliza o template Object.ascx para renderizar.

View

Dado o cenário acima, a idéia é que nossa View renderize um formulário para atualizar os dados dos alunos de uma determinada turma, através da classe ListaPresenca. O código da View está demonstrado abaixo:

@model TesteTemplate.Models.ListaPresenca

@{
ViewBag.Title = "Index";
}

<h2>Index</h2>

<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>

@using (Html.BeginForm()) {
@Html.ValidationSummary(true)
<fieldset>
<legend>ListaPresenca</legend>

<div class="editor-label">
@Html.LabelFor(model => model.NomeInstrutor)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.NomeInstrutor)
@Html.ValidationMessageFor(model => model.NomeInstrutor)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Alunos, "CollectionAlunos")
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}

Na linha 25 estamos dizendo ao ASP.NET MVC para utilizar o template CollectionAlunos.ascx para renderizar a propriedade Alunos da classe ListaPresenca. Esse template ainda não existe em nosso projeto, portanto será utilizado o template Object.ascx. Para que isso funcione, basta criarmos o CollectionAlunos.ascx dentro da pasta Views\Shared\EditorTemplates de nosso projeto. O código do template (bem simples) será algo mais ou menos assim:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<table>
<thead>
<%
if (Model != null)
{
%>
<tr>
<th>Nome</th>
<th>Endereço</th>
</tr>
</thead>
<tbody>
<%
string oldPrefix = ViewData.TemplateInfo.HtmlFieldPrefix;
int index = 0;

ViewData.TemplateInfo.HtmlFieldPrefix = String.Empty;

foreach (Object item in (IEnumerable)Model) {
string fieldName = String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}[{1}]", oldPrefix, index++);
ViewContext.Writer.Write(Html.EditorFor(m => item, null, fieldName));
}

ViewData.TemplateInfo.HtmlFieldPrefix = oldPrefix;
}
%>
</tbody>
</table>

Na linha 22, estamos utilizando o comando Html.EditorFor(m => item, null, fieldName). Este item que está sendo passado para o Helper é um objeto do tipo Aluno, como vocês podem ver na imagem abaixo:

No entanto, não estamos dizendo para o framework qual controle .ascx ele deve utilizar (estamos passando null no segundo parâmetro). Por convenção, nesse caso, ele assume que deve ser utilizado o Aluno.ascx ou Object.ascx. Vamos então criar o template Aluno.ascx dentro da pasta Views\Shared\EditorTemplates:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<TesteTemplate.Models.Aluno>" %>
<tr>
<td>
<%
ViewContext.Writer.Write(Html.EditorFor(m => m.Nome));
%>
</td>
<td>
<%
ViewContext.Writer.Write(Html.EditorFor(m => m.Endereco));
%>
</td>
</tr>

Feito isso, o resultado será algo do tipo:

Quando clicamos no botão Save, realizamos um Post. No controller, ao verificar o objeto ListaPresenca, podemos ver que o mesmo foi devidamente populado através do Model Binding do ASP.NET MVC:

Quando isso é útil? Quando temos um objeto complexo que será renderizado em várias Views do mesmo software. Ao invés de duplicamos código para fazer a renderização desse tipo em cada uma das Views, basta criar um template para renderizá-lo e utilizar o helper Html.EditorFor.

Espero que tenham gostado da dica, até a próxima.

Osmar Landin