ASP.Net MVC: Html.Display() for object in a Collection

Chris Moschini picture Chris Moschini · Oct 19, 2010 · Viewed 16k times · Source

The standard MVC example to draw an item with the appropriate View Template is:

Html.DisplayFor(m => m.Date)

If the Model object has a property named Date of type DateTime, this returns a string with the HTML from the Display/DateTime.ascx template.

Suppose you wanted to do the same thing, but couldn't use the strongly-typed version - you didn't know the Model's type for this View at compile time. You use the older:

Html.Display("Date");

So here's the hard part.

Suppose the Model is IEnumerable. You don't know what those objects are at compile-time, but at run-time they happen to be objects with a Date of type DateTime again, like:

public class ModelClass
{
    public DateTime Date { get; set; }
}

Now suppose you want your View to iterate over those objects and render each out. If all you cared about was the value you could do this:

<%
StringBuilder sb = new StringBuilder();
foreach(object obj in (IEnumerable<object>)Model)
{
    Type type = obj.GetType();

    foreach(PropertyInfo prop in type.GetProperties())
    {
        // TODO: Draw the appropriate Display PartialView/Template instead
        sb.AppendLine(prop.GetValue(obj, null).ToString());
    }
}
%>
<%= sb.ToString() %>

I'm obviously taking some shortcuts to keep this example focused.

Here's the point - how do I fulfill that TODO I've written for myself? I don't just want to get the value - I want to get it nicely formatted like Html.Display("Date"). But if I just call Html.Display("Date"), it inspects the Model, which is an IEnumerable, for a property named Date, which it of course does not have. Html.Display doesn't take an object as an argument to use as the Model (like Html.Display(obj, "Date"), and all the classes and methods I can find that lie underneath appear to be internal so I can't tweak and call into them directly.

There must be some simple way to accomplish what I'm trying to do, but I can't seem to find it.

Just to make sure I'm being clear - here's an example of the code of DateTime.ascx:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<System.DateTime>" %>
<%= Model.ToString("MM/dd/yyyy") %>

And so, ideally, the output from this View that can take any Model, but in this case a list of 3 of these ModelClass objects above, would be:

11/10/2001 11/10/2002 11/10/2003

Because the code would find the Display PartialView for DateTime and render it appropriately for each.

So - how do I fulfill the TODO?

Answer

Clicktricity picture Clicktricity · Oct 20, 2010

Have a look at the template code in this excellent post from Phil Haack. It seems to come close to what you are looking for: http://haacked.com/archive/2010/05/05/asp-net-mvc-tabular-display-template.aspx