37

I am loving MVC 2. The whole thing just fits the web so well.

There is one piece of functionality, however, that I am unable to coax out of the Html.DisplayFor() function:

<@ Page Inherits="ViewPage<IEnumerable<Foo>>">

<% foreach(var item in Model) { %>

    <%: Html.DisplayFor(item.BarBaz) %>

<% } %>

I need to be able to use the DisplayTemplate for this value. Is there a way to do this?

John Gietzen
  • 48,783
  • 32
  • 145
  • 190

6 Answers6

70

Actually, I figured it out. How stupid of me.

This works:

<@ Page Inherits="ViewPage<IEnumerable<Foo>>">

<% foreach(var item in Model) { %>

    <%: Html.DisplayFor(m => item.BarBaz) %>

<% } %>

However, this will not work correctly for Html.HiddenFor and Html.ValueFor. In particular, Html.HiddenFor(m => item.NullableDecimal) will render as <input name="NullableDecimal" value="0" /> and Html.ValueFor(m => item.NullableDecimal, "0.00##) will render as 0.00##. However, if you apply a [DisplayFormat(DataFormatString = "{0:0.00########}" to your view model, then it will suddenly work. For this reason, you're probably best off using Html.Display, Html.Hidden, and Html.Value extensions, since you're less likely to run into scenarios where things fail when someone makes a non-local change.

John Zabroski
  • 2,212
  • 2
  • 28
  • 54
John Gietzen
  • 48,783
  • 32
  • 145
  • 190
  • 2
    I think, I am even more stupid, as I am not sure how to interpret this. The lambda formal parameter m is not used in the expression. So is this only there because it is required by _DisplayFor_? Also, I expected that `@Html.Display(item.BarBaz)` would work just as well, but that renders nothing. – R. Schreurs Sep 05 '13 at 11:10
  • @R.Schreurs It seems the lambda needs to be a `Func` expression that takes the model's type as a parameter, but it can return anything. – JohnnyHK Apr 03 '14 at 03:40
12

You can accomplish it by getting away from the foreach and using a regular for loop:

 <% for (int i = 0; i < Model.Count(); i++) { %>

    <%: Html.DisplayFor(p=> p.ElementAt(i).BarBaz) %>

 <%} %>

Another option would be to create a PartialView that took an Foo object and only displayed the information that you wanted.

<% foreach (var item in Model)
   {
       Html.RenderPartial("FooBarBazLine", item);
   } %>
sgriffinusa
  • 4,203
  • 1
  • 25
  • 26
  • 1
    I agree that option 2 is a better solution. I just included option 1 as I thought of it first and didn't really have any reason to remove it as it does solve the problem. – sgriffinusa Aug 06 '10 at 03:20
7

This is an old question, but i guess that someone can get benefits from my solution:

Aspx view

<%@ Page Inherits="ViewPage<IEnumerable<Foo>>" %>

<% foreach (var item in Model) { %>

    <%: Html.DisplayFor(m => item) %>

<% } %>

Razor view

@model IEnumerable<Foo>

@foreach (var item in Model)
{
    Html.DisplayFor(m => item)
}

Since DisplayFor accepts an implicit typed lambda, we can directly indicate the instance to display in the loop.

Finally, i'm agree with Anders E. Andersen's answer for the template usage

Community
  • 1
  • 1
T-moty
  • 2,679
  • 1
  • 26
  • 31
  • Thanks for sharing =D I was sooo focused on always having to go (x => Model.Yada) inside the expression... sometimes you just need someone else to open you eyes =D Thanks again! – Markus Knappen Johansson Jan 22 '20 at 07:39
3

Html.DisplayFor can automatically iterate over collections, displaying a partial view for each element in the collection.

The first thing you need to do is create an actual model class, with the collection being a property of the class.

public class Bar
{
    public IEnumerable<Foo> foo { get; set; }
}

Return this class from your controller instead of the raw collection.

Secondly you need a display template for the Foo class. Display templates are partial views that need to be placed in the folder Views/Shared/DisplayTemplates.

Edit: You can have them in your controller subfolder of Views as well if you want to limit the template to a particular controller. See this question for more information.

Here is an example in razor syntax:

@model YourNameSpace.Foo
<p>@Model.BarBaz</p>

Save it as Foo.cshtml in the DisplayTemplates folder given above.

This template is pretty simple because it is based on your example where you are only displaying a string, but if the collection elements where a class with its own properties you could create a more elaborate template.

Now in the original view, you can get rid of the loop entirely and just write

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

Notice foo is the name of the property in your new model class that contains the old collection you looped over before.

DisplayFor will automatically know that the foo property is of type (collection of) Foo and pick up the Foo.cshtml template in the DisplayTemplates folder and show it once for each element in foo.

Community
  • 1
  • 1
Anders E. Andersen
  • 1,635
  • 2
  • 14
  • 20
  • Imagine you have tons of models, having lists like this **IEnumerable**. Are you going to create one template per one-of-thousand-entities? All you need is just generic template for basic types, like string, integer, boolean, etc.. – Alexander Oct 01 '15 at 12:31
  • You don't really need bar do you, in the event the foo collection is accessible, say, statically? Or it is just a local variable? – toddmo Jan 02 '17 at 00:13
1

In Razor I am using

@Html.DisplayFor(model => item.CreatedDate)
Mariusz Jamro
  • 30,615
  • 24
  • 120
  • 162
0

Just be careful that a DisplayFor on a POST of a form doesnt actually create a field that can be referenced back in the controller. You would need to use an additional HiddenFor if you need to refer to fields in your submitted POST method.