问题
I'm having a problem with display templates and dealing with interfaces and objects which implement the interface. In the example I have many objects, which I want to be rendered in a fixed way, I decided to create an interface and reference this in the view which I've decided to put into the shared display templates folder. DisplayFor
doesn't seam to work for objects passed to it which implement the interface in the view, does any one know a solution to this.
Its probably easier to explain via code so I've wrote a quick example. The base interface and two classes which inherit from it:
public interface IPet
{
String Name { get; }
}
public class Dog : IPet
{
public String Name { get; set; }
}
public class Cat : IPet
{
public String Name { get; set; }
}
The example display template in shared display templates
@model IPet
<div>@Model.Name</div>
The example view model to be passed to the view
public class VM
{
public IPet Master { get; set; }
public IEnumerable<IPet> Minions { get; set; }
}
The controller (in this case to create mock information)
public ActionResult Index()
{
var viewModel = new VM();
viewModel.Master = new Cat(){Name = "Fluffy"};
var minions = new List<IPet>();
minions.Add(new Dog(){Name = "Dave"});
minions.Add(new Dog(){Name = "Pete"});
minions.Add(new Cat(){Name = "Alice"});
viewModel.Minions = minions;
return View(viewModel);
}
and finally the view which I would expect DisplayFor
to work
@model ViewInheritance.Models.VM
<h2>Master</h2>
@Html.DisplayFor(x => x.Master)
<h2>Minions</h2>
@Html.DisplayFor(x => x.Minions)
Given that all the objects are are defined in the view model as the interfaces, howcome it fails to use the display template?
One solution I have found is to simply use the code
@Html.DisplayFor(x => x.Master, "IPet")
To recap, the question is:
Why does this happen?
Is there a way to make DisplayFor
correctly work out that a type of Cat
which implements IPet
should in fact be looking at the common shared view IPet.cshtml
?
Thanks
回答1:
Starting a new MVC application and fixing the code to actually compile the view renders fine. It also renders fine when moving the view into the shared folder.
I Added setter to IPet:
public interface IPet
{
String Name { get; set; }
}
I updated implementation and added public accessors:
public class Dog : IPet
{
public String Name { get; set; }
}
public class Cat : IPet
{
public String Name { get; set; }
}
I left your VM alone and also did not change any code in your View. Pressing F5, running the MVC application rendered the results as expected (See image).
回答2:
Unfortunately, I don't think ASP.NET MVC currently supports automatically selecting templates based on implemented interfaces. I think this makes sense because a class could implement multiple interfaces, so if you had templates for more than one of those interfaces, which one should the framework choose?
You could use a base class instead of an interface if your design can cope with it:
- Change
IPet
to a (possibly abstract)class
. - Change
IPet.cshtml
toPet.cshtml
.
Otherwise I think you'll just need to explicitly tell the framework which template to use. Here are some options:
- Decorate the view model properties with
[UIHint]
. - Specify the template in your calls to your
HtmlHelper
methods such asDisplayFor
. - Make your own
ModelMetadataProvider
and change theTemplateHint
property of the resultingModelMetadata
.
来源:https://stackoverflow.com/questions/9127772/display-template-not-used-for-interface