Polymorphic collection and rendering of ViewModel in partial MVC views

I am having a problem with the polymorphic ViewModels collection in my MVC application. I got this through a web service call, and I need to iterate through them and give them my own partial view based on the type of the object.

public abstract class ProvinceViewModel { public string Code { get; set; } } public sealed class OntarioViewModel : ProvinceViewModel { } public sealed class QuebecViewModel : ProvinceViewModel {} 

In my opinion, I am trying to iterate through them and assign a partial view. I need to do many types to make it work. If I try to move this to a controller action and pass an abstract type, I get an error that we cannot create an instance of the abstract class.

 ICollection<ProvinceViewModel> ProvinceList; // collection receive via service @for (int i = 0, c = ProvinceList.Count; i < c; i++) { var currentProvince = this.Model.ElementAt(i); @switch (additionalRegistry.Code) { case "QC": @Html.Partial("AlbertaDetail", (QuebecViewModel)currentProvince) break; case "ON": @Html.Partial("OntarioDetail", (OntarioViewModel)currentProvince) break; default: @Html.Partial("ProvinceDetail", ProvinceViewModel) break; } } 

I have a strongly View type, so that I can access various properties.

How can I solve this more elegantly? Should I create a new surrogate base class for an abstract class to make it easier to instantiate?

+5
source share
3 answers

When faced with the same problem in the past, I created the following solution:

First, decorate your (specific) view model with the ExportMetadata attribute, which indicates the name of the view to be used. For instance:

 [ExportMetadata("View", "Ontario")] public sealed class OntarioViewModel : ProvinceViewModel { } [ExportMetadata("View", "Quebec")] public sealed class QuebecViewModel : ProvinceViewModel {} 

Then add the HtmlHelper to the following Partial method:

 public static MvcHtmlString Partial<T>(this HtmlHelper htmlHelper, T model, string prefix = null) { var modelType = typeof (T); var partialAttr = modelType.GetCustomAttributes<ExportMetadataAttribute>().SingleOrDefault(x => x.Name == "View"); if (partialAttr == null) throw new Exception(modelType.Name + " doesn't define any view to be used"); var partialName = (prefix ?? String.Empty) + partialAttr.Value; return htmlHelper.Partial(partialName, model, htmlHelper.ViewData); } 

Then use it:

 @Html.Partial(currentProvince); 

And in case your partial files are in some subdirectory:

 @Html.Partial(currentProvince, "Partials/") 

(If you need help registering a custom HTML helper, see fooobar.com/questions/118700 / ... )

+1
source

I had a similar requirement, and this is how I managed to solve this problem. My view model (BusinessEventEmailViewModel) has a list of interfaces (IBusinessEventEmail) allowed at runtime with unity. IBusinessEventEmail has an EventCode property.

 public class BusinessEventEmailViewModel : MailHeaderViewModel { #region members public List<IBusinessEventEmail> Events { get; set; } 

In my opinion, I present a partial view using a naming convention:

 Html.RenderPartial("~/Views/Shared/Email/_" + businessEvent.EventCode + ".cshtml", businessEvent); 

Then I have XXXEventEmail implementing IBusinessEventEmail with EventCode XXX and partial view _XXX.cshtml

0
source

You can achieve this with display templates. Create a display template for each type in the DisplayTemplates folder in your dispatcher views directory:

 +-- Views +-- Provinces +-- DisplayTemplates +-- OntarioViewModel.cshtml +-- QuebecViewModel.cshtml 

Display each model with the DisplayFor helper in your view:

 @model ICollection<ProvinceViewModel> @foreach (var province in Model) { @Html.DisplayFor(_ => province) } 
0
source

All Articles