19

I have this line in my view

@(Html.DisplayFor(m => m.DaysOfWeek, "_CourseTableDayOfWeek"))

where m.DaysOfWeek is a IEnumerable<DateTime>.

There is the content of _CourseTableDayOfWeek.cshtml:

@model DateTime
@{
    ViewBag.Title = "CourseTableDayOfWeek";
}
<th>
    @System.Globalization.CultureInfo.CurrentCulture.DateTimeFormat.DayNames[(int) Model.DayOfWeek]
    <span class="dateString">Model.ToString("G")</span>
</th>

And I get the following error:

The model item passed into the dictionary is of type 'System.Collections.Generic.List`1[System.DateTime]', but this dictionary requires a model item of type 'System.DateTime'.

If I refer to the following post:

https://stackoverflow.com/a/5652524/277067

The DisplayFor should be looping through the IEnumerable and display the template for each item, shouldn't it?

Community
  • 1
  • 1
remi bourgarel
  • 9,231
  • 4
  • 40
  • 73

4 Answers4

24

It's not looping because you have specified a name for the display template as second argument of the DisplayFor helper (_CourseTableDayOfWeek).

It loops only when you rely on conventions i.e.

@Html.DisplayFor(m => m.DaysOfWeek)

and then inside ~/Views/Shared/DisplayTemplates/DateTime.cshtml:

@model DateTime
@{
    ViewBag.Title = "CourseTableDayOfWeek";
}
<th>
    @System.Globalization.CultureInfo.CurrentCulture.DateTimeFormat.DayNames[(int) Model.DayOfWeek]
    <span class="dateString">Model.ToString("G")</span>
</th>

Once you specify a custom name for the display template (either as second argument of the DisplayFor helper or as [UIHint] attribute) it will no longer loop for collection properties and the template will simply be passed the IEnumerable<T> as model.

It's confusing but that's how it is. I don't like it either.

Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
0

This seems like a bug. Html Helper classes are easy to extend, although after looking at the MVC source, looking for the bug, I gave up and just leveraged the premise that templates work for an individual item, so I wrote an HtmlHelper extension that wraps it for you. I took out the lambda expression for my own simplicity, but you can easily go back to that. This example is just for a list of strings.

    public static class DisplayTextListExtension
{
    public static MvcHtmlString DisplayForList<TModel>(this HtmlHelper<TModel> html, IEnumerable<string> model, string templateName)
    {
        var tempResult = new StringBuilder();

        foreach (var item in model)
        {
            tempResult.Append(html.DisplayFor(m => item, templateName));
        }

        return MvcHtmlString.Create(tempResult.ToString());
    }
}

Then the actual usage looks like:

                                @Html.DisplayForList(Model.Organizations, "infoBtn")
Dan Csharpster
  • 2,662
  • 1
  • 26
  • 50
0

A minor tweak to allow this to Dan's solution to be a little more generic:

public static class DisplayTextListExtension
    {
        public static MvcHtmlString DisplayForList<TModel, 
EModel>(this HtmlHelper<TModel> html, IEnumerable<EModel> model, string templateName)
        {
            var tempResult = new StringBuilder();

            foreach (var item in model)
            {
                tempResult.Append(html.DisplayFor(m => item, templateName));
            }

            return MvcHtmlString.Create(tempResult.ToString());
        }
    }
Georgy
  • 12,464
  • 7
  • 65
  • 73
-1

Use FilterUIHint instead of the regular UIHint, on the IEnumerable<T> property.

public class MyModel
{
    [FilterUIHint("_CourseTableDayOfWeek")]
    public IEnumerable<DateTime> DaysOfWeek { get; set; }
}

No need for anything else.

@Html.DisplayFor(m => m.DaysOfWeek)

This now displays an "_CourseTableDayOfWeek" EditorTemplate for each DateTime in DaysOfWeek.

Apache
  • 619
  • 6
  • 22
  • I couldn't get this to work. It just ignored the FilterUIHint, like there was no annotation. Also you used DisplayFor but said EditorTemplate for each; was that a mistake or on purpose? – Tod Aug 19 '16 at 08:33