开发者

How to create a html helper method similar to Html.DisplayFor that will look in a custom folder

My model is based on an abstract base class, and it looks like this:

  • InsurancePolicy (base abstract class for all policies)
    • VehicleInsurancePolicy
      • AbcInsurancePolicy
      • DefInsurancePolicy
    • HomeInsurancePolicy
    • LifeInsurancePolicy
    • ... etc

In a controller I have the following code for the usual detalis / list scenarios:

    public ActionResult Details(int id) {
        // actual type is one of the concrete implementations
        InsurancePolicy policy = _repository.Get(id);
        return View(policy);
    }

    public A开发者_Python百科ctionResult List() {
        // a list of concrete implementations
        IList<InsurancePolicy> policies = _repository.GetLatest(20);
        return View(policies);
    }

The Details.chstml only calls @Html.DisplayForModel(), and ASP.NET MVC actually selects the correct partial view from Shared/DisplayTemplates based on the actual type (one of the subclasses). Moreover, if it cannot find a view with that name, it falls back to the base class template, InsurancePolicy.cshtml. For example, it would select VehicleInsurancePolicy.cshtml for a AbcInsurancePolicy model, because no AbcInsurancePolicy.cshtml is found; for HomeInsurancePolicy it will select the default InsurancePolicy.cshtml since there is no HomeInsurancePolicy.cshtml in DisplayTemplates folder. It allows me to add other InsurancePolicy subtypes, without creating a display template in the first place.

I want to have similar behavior for a list view. In List.cshtml razor view I would only loop over my items, and call something like Html.ListItemFor(...)

<ul>
@foreach (var policy in Model) {
    @Html.ListItemFor(_ => policy)
 }
</ul>

How do I create a Html.ListItemFor html helper, that would look into a ListItemTemplates folder and act exactly as Html.DisplayFor ? Is there any other way to do this?

Notes: Html.DisplayFor(m => m.Property, templateName) wouldn't work, it must choose the template name based on item type, fall back recursively to base class template until it finds something.


The way this works is that templates are found based on a set of view locations defined in the view engine.

What you can do is create your own ViewEngine by inheriting from the RazorViewEngine and overriding the

   public override ViewEngineResult FindPartialView(
        ControllerContext controllerContext, 
        string partialViewName, 
        bool useCache)

this way you can come up with your own completely new convention and use the values in the controller context to decide what view to look for.

You could probably do something like this (altho might need to tweak it to suit your scenario exactly)

 public override ViewEngineResult FindPartialView(
    ControllerContext controllerContext, 
    string partialViewName, 
    bool useCache)
{
    ViewEngineResult result = null;

    //Get the TypeName that you want to look for
     var typeName = controllerContext.Controller.ViewData.Model.GetType().Name;

     result = base.FindPartialView(
             controllerContext, typeName, useCache);


    //Fall back to default search path if no other view has been selected  
    if (result == null || result.View == null)
    {
        result = base.FindPartialView(
            controllerContext, partialViewName, useCache);
    }

    return result;  
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜