Asp.net mvc ASP.NET MVC:使用接口作为模型创建动态视图

Asp.net mvc ASP.NET MVC:使用接口作为模型创建动态视图,asp.net-mvc,asp.net-mvc-5,Asp.net Mvc,Asp.net Mvc 5,我有一个这样的界面: public interface ISettings { string Name { get; } } 以及各种实现,例如: public class SettingsA : ISettings { public string Name { get { return "Example A"; } } public string CustomAttributeA1 { get; set; } public stri

我有一个这样的界面:

public interface ISettings
    {
        string Name { get; }
    }
以及各种实现,例如:

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; }
}
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; }
}
现在我的问题是需要创建一个动态UI(将ISettings作为模型传递,并根据每个实现的属性生成控件)。所以我有这个:

Edit.cshtml:

@model ISettings

@Html.EditorForModel(Model)

这可以很好地生成所需字段,但问题是我无法轻松地对其进行自定义(例如:为引导添加css类或我可能需要的任何其他css)。。我想我可以使用基于HTML标记的CSS选择器。。但这似乎不是理想的方法。对于这个问题有什么好的解决方法吗?处理这种情况的最佳方法是什么?

尝试使用一些HTML帮助程序,例如:

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 "";
   }
}
以前从未这样做过,但应该可以很好地工作

您将得到类似于:

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

检查我的CSS类!
您可以在IDisposable上玩一点,玩一些很酷的东西,比如:

@using(var div = Html.BeginDivWithCustomCssClassPerType())
{
   <span>Check my CSS classes!</span>
}
@使用(var div=Html.BeginDivWithCustomCssClassPerType())
{
检查我的CSS类!
}
这些扩展实际上可以帮助您使用
编辑器for
。您可以保留您的
EditorFor
调用,该调用将为
ISettings
呈现一个EditTemplate,您将在那里放置扩展方法,以在不同的继承情况下提供不同的行为


另一种方法是简单地使用不同的
EditTemplate
元素,每个具体类型(继承)对应一个元素,如果它们彼此差异很大,则使用特定的模板名称调用您的
EditorFor

我很久以前就找到了一个解决方案,但忘了在这里发帖。。。我不能发布我所做的一切,但基本思想是向
ISettings
界面添加一个属性,如:

public interface ISettings
{
    string Name { get; }

    string EditorTemplatePath { get; }
}
然后,您需要在每个类中重写它。例如:

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; }
}
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; }
}
当然,您需要在某个地方的控制器中实现自己的逻辑,以返回自定义HTML。对我来说,我使用AJAX并执行如下操作:

[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);
}
仅供参考,
RenderRazorPartialViewToString
位于我的基本控制器中,定义如下:

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();
    }
}

解决方案的大致思路是编写一个基于反射的css类生成器。您可以编写自己的html扩展并获得所需的结果。例如:@Html.MattEditorForModel(Model)。这是一个非常简单和干净的方法。谢谢你的回复,但问题是我不知道什么类型将提前。它们来自插件,因此无法事先知道属性是什么。我认为解决这个问题的唯一方法可能是在我的界面中添加一些GetHtml()方法。。因此,每个实现都可以提供一个独特的UI。您的核心模块无法猜测插件方法-这就是为什么您提供定义良好的插件契约。您大概猜到
GetHtml()
是正确的。用类似的东西定义一个契约,并在某个
try catch
中调用该方法,因为谁知道插件开发人员是否(没有)在任何地方放置一个
throw NotImplementedException()
来补充:方法契约不是唯一的方法。例如,您可以定义一个属性并对我建议的扩展类使用反射来获取特定类型的自定义属性,如
[OutputCssClass('my-css-class')]
或其他。这取决于您,尽管方法调用比反射查找属性快得多。我也意识到,这不仅仅是CSS,而是像数据这样的东西,每个都需要不同。。例如:为下拉列表指定选择列表项。我目前正在研究是否可以使用插件类库中的一些EditorTemplates。如果没有,我想我唯一能做的就是使用我上面建议的方法。尽管如此,还是要感谢您的建议。我们的一个实现使客户能够将新字段/项目添加到我们应用程序的现有页面中。为此,我们有一个定义可能性的
IPlugin
合同(例如:
SupportsCustomFields
supportscustomcs
,等等)。加载特定于租户的插件时,我们获取所有插件并建立缓存。之后,当呈现我们的页面(插件开发人员可用的路由相关常量)时,我们检查插件是否支持添加字段,如果支持,我们将通过特定契约加载该字段(例如:
IPluginFieldProvider.GetFieldsFor(字符串路由)
)。