Question

So I have an interface like so:

public interface ISettings
    {
        string Name { get; }
    }

and various implementations, for example:

public class SettingsA : ISettings
{
    public string Name { get { return "Example A"; } }

    public string CustomAttributeA1 { get; set; }

    public string CustomAttributeA2 { get; set; }
}

public class SettingsB : ISettings
{
    public string Name { get { return "Example B"; } }

    public string CustomAttributeB1 { get; set; }

    public string CustomAttributeB2 { get; set; }
}

Now I have the problem of needing to create a dynamic UI (pass ISettings as the model and generate controls based on the properties of each implementation). So I have this:

Edit.cshtml:

@model ISettings

@Html.EditorForModel(Model)

This is producing the required fields nicely, but the problem with this is I cannot easily customize it (example: add css classes for Bootstrap or whatever other CSS I might want).. I suppose I could use CSS selectors based on HTML tag.. but doesn't seem like the ideal approach. Is there some nice workaround to this issue? What's the best way to deal with this situation?

Was it helpful?

Solution 2

I found a solution long ago and forgot to post back here... I can't post everything I did, but the basic idea is to add a property to the ISettings interface like:

public interface ISettings
{
    string Name { get; }

    string EditorTemplatePath { get; }
}

You then need to override this in each class. For example:

public class SettingsA : ISettings
{
    public string Name { get { return "Example A"; } }

    public string EditorTemplatePath
    {
        get{ return "~/Views/Shared/EditorTemplates/SettingsA.cshtml"; } 
    }

    public string CustomAttributeA1 { get; set; }

    public string CustomAttributeA2 { get; set; }
}

public class SettingsB : ISettings
{
    public string Name { get { return "Example B"; } }

    public string EditorTemplatePath
    {
        get{ return "~/Views/Shared/EditorTemplates/SettingsB.cshtml"; } 
    }

    public string CustomAttributeB1 { get; set; }

    public string CustomAttributeB2 { get; set; }
}

And of course you need to implement your own logic in a controller somewhere to return the custom HTML. For me, I use AJAX and do something like this:

[Route("get-editor-ui/{type}")]
public ActionResult GetEditorUI(string type)
{
    var model = // logic to get the model from "type" argument

    if (model == null)
    {
        return HttpNotFound();
    }

    string content = RenderRazorPartialViewToString(model.EditorTemplatePath, model);
    return Json(new { Content = content }, JsonRequestBehavior.AllowGet);
}

FYI, RenderRazorPartialViewToString is in my base controller and is defined as follows:

public string RenderRazorPartialViewToString(string viewName, object model)
{
    ViewData.Model = model;
    using (var sw = new StringWriter())
    {
        var viewResult = System.Web.Mvc.ViewEngines.Engines.FindPartialView(ControllerContext, viewName);
        var viewContext = new ViewContext(ControllerContext, viewResult.View, ViewData, TempData, sw);
        viewResult.View.Render(viewContext, sw);
        viewResult.ViewEngine.ReleaseView(ControllerContext, viewResult.View);
        return sw.GetStringBuilder().ToString();
    }
}

OTHER TIPS

Try with some HTML helpers, like:

public static class MyHtmlExtensions
{
   public static string GetCssForMyDiv(this HtmlHelper htmlHelper)
   {
      if (htmlHelper.ViewData.Model is SettingsA) return "class-a";
      else if (htmlHelper.ViewData.Model is SettingsB) return "class-b";
      else return "";
   }
}

Never done this before but should work quite fine.

You'll have something like:

<div class="class-base my-class my-div-style @Html.GetCssForMyDiv()">
   Check my CSS classes!
</div>

You may play a little with IDisposable and have something cool as:

@using(var div = Html.BeginDivWithCustomCssClassPerType())
{
   <span>Check my CSS classes!</span>
}

These extensions might actually help you with your EditorFor. You might keep your EditorFor call which will render an EditTemplate for ISettings and there you'll place extension methods to provide diferent behavior on diferent inheritance cases.

Another approach is to simply have diferent EditTemplate elements, one for each concrete type (inheritance), if they differ a lot from each other, and call your EditorFor with the specific template name.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top