ASP.NET 属性的 MVC 编辑器模板



通常我通过@Html.RenderModel渲染我的表单,但这次我有一个复杂的渲染逻辑,我手动渲染它。我决定为一个属性创建一个编辑器模板。下面是代码(从默认对象编辑器模板实现复制粘贴(:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<% var modelMetadata = ViewData.ModelMetadata; %>
<% if (modelMetadata.HideSurroundingHtml) { %>
    <%= Html.Editor(modelMetadata.PropertyName) %>
<% } else { %>
    <% if (!String.IsNullOrEmpty(Html.Label(modelMetadata.PropertyName).ToHtmlString())) { %>
        <div class="editor-label"><%= Html.Label(modelMetadata.PropertyName) %></div>
    <% } %>
    <div class="editor-field">
        <%= Html.Editor(modelMetadata.PropertyName) %>
        <%= Html.ValidationMessage(modelMetadata.PropertyName) %>
    </div>
<% } %>

这是我如何使用它:

@Html.EditorFor(x => x.SomeProperty, "Property") //"Property" is template above

但它没有奏效:无论显示名称如何,标签都会呈现,编辑器根本不呈现(在 Watches Html.Editor(modelMetadata.PropertyName 中显示空字符串(。我做错了什么?

您正在从编辑器调用编辑器。正如@RPM1984在此答案的评论中改写@darin-dmitrov:在给定的视图特定上下文中,对于给定类型,您只能在运行时使用1个模板

如果您将视图更改为渲染文本框而不是编辑器,它可以工作,我刚刚尝试过:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<% var modelMetadata = ViewData.ModelMetadata; %>
<% if (modelMetadata.HideSurroundingHtml)
   { %>
   <%= Html.Editor(modelMetadata.PropertyName) %>
<% }
   else
   { %>
   <% if (!String.IsNullOrEmpty(modelMetadata.DisplayName))
       { %>
       <div class="editor-label"><%= Html.Label(modelMetadata.PropertyName) %></div>
    <% } %>
    <div class="editor-field"><%= Html.TextBox(modelMetadata.PropertyName) %> <%= Html.ValidationMessage(modelMetadata.PropertyName) %></div>
<% } %>

如果要呈现其他内容而不是文本框(即下拉列表(,则需要在该属性的模板中确定并呈现它。或者,如果您对更多编辑器有一些共同点,我通常会将其提取到共享文件夹中的部分视图中,然后只使用Html.Partial("ViewName")

而且,关于无论显示名称如何都呈现标签,为了防止标签在没有显示名称的情况下呈现,请将 if 条件更改为 !String.IsNullOrEmpty(modelMetadata.DisplayName)(我已经在主代码块中这样说了(

编辑此编辑涉及与 object.ascx 默认编辑器模板相关的问题。这是object.ascx的代码,取自Brad Wilson的博客:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<% if (ViewData.TemplateInfo.TemplateDepth > 1) { %>
    <%= ViewData.ModelMetadata.SimpleDisplayText%>
<% }
   else { %>    
    <% foreach (var prop in ViewData.ModelMetadata.Properties.Where(pm => pm.ShowForEdit
                         && !ViewData.TemplateInfo.Visited(pm))) { %>
        <% if (prop.HideSurroundingHtml) { %>
            <%= Html.Editor(prop.PropertyName) %>
        <% }
           else { %>
            <% if (!String.IsNullOrEmpty(Html.Label(prop.PropertyName).ToHtmlString())) { %>
                <div class="editor-label"><%= Html.Label(prop.PropertyName) %></div>
            <% } %>
            <div class="editor-field">
                <%= Html.Editor(prop.PropertyName) %>
                <%= Html.ValidationMessage(prop.PropertyName, "*") %>
            </div>
        <% } %>
    <% } %>
<% } %>

此代码确实从编辑器内部调用 Html.Editor,但在为复杂模型的属性创建编辑器列表的循环中。这些调用中的每一个都将调用相应的编辑器(即对于字符串,它将在那里显示 string.ascx 等(,并且仅当您有一些不是字符串的"未知"属性并且没有特定的编辑器(即 byte[](时,它将为其调用 object.ascx,但这不是为当前属性调用编辑器(您正在尝试执行的操作(:

对象

模板的主要职责是显示复杂对象的所有属性以及每个属性的标签。但是,它还负责显示模型的 NullDisplayText 的值(如果为 null(,并且还负责确保仅显示一个级别的属性(也称为对象的"浅潜"(。在下一篇博文中,我们将讨论自定义此模板的方法,包括执行"深入探讨"操作。


总结

简短版本:

同一属性的更多编辑器基本上是功能差异的解决方案("对于是/否,我想要这里的单选组和那里的下拉列表(",对于视觉差异,应该使用部分视图,因为您可以根据需要嵌套它们,因为您按名称显式调用它们,因此没有施加任何限制,您有责任防止任何潜在的递归。

长版本:

我一直在调查这个问题,因为我有同样的问题,我正在使用编辑器模板来呈现<li><td>元素(取决于配置/主题(,并从内部调用另一个包含标签和输入的编辑器(两种情况相同,但如果属性为 bool,则输入在标签之前(,我再次调用第三个模板进行输入(以防止重复标签/输入和输入/标签场景的代码(, 但这行不通。虽然我没有在 msdn 或其他相关来源上找到解释,但我发现编辑器什么都不给出的唯一情况是当您想要为当前编辑器上下文的属性呈现编辑器时(所以它实际上正是我已经引用的:"对于给定类型,您只能在运行时使用 1 个模板, 在给定的视图特定上下文中。在对此进行了更多思考之后,我相信现在他们施加此限制是正确的,因为属性 x 只能使用一个编辑器进行渲染。您可以根据需要为属性 x 设置任意数量的编辑器,但不能使用多个模板一次呈现一个属性。任何用于渲染属性 x 的模板都可以使用其他模板来渲染属性 x 的 PARTS,但不能多次对 x 使用(相同或不同的(编辑器(相同的逻辑适用于在同一模型上具有两个或多个属性 x(相同的类型和名称(。

此外,如果您可以将当前属性的另一个模板插入到当前模板中,则可以为当前属性链接任意数量的模板,并且很容易导致递归,因此无论如何它会导致您堆栈溢出:)

"x.SomeProperty"是什么类型?我现在假设它的类型称为属性。

我的模型.cs

public class MyModel
{
    public Property SomeProperty { get; set; }
}

属性.cs

public class Property
{
    [Display(Name="Foo")]
    public int Value { get; set; }
}

Views/Shared/EditorTemplates/Property.cshtml

@model MvcApplication1.Models.Property
@Html.LabelFor(model => model.Value)
@Html.EditorFor(model => model.Value)
@Html.ValidationMessageFor(model => model.Value)

MyView.cshtml

@Html.EditorFor(model=>model.SomeProperty)

如果您没有为 EditorFor helper 提供模板名称,它会查找名称与 SomeProperty 的类型匹配的编辑器模板。

更新:

要为字符串创建自定义编辑器模板,请执行以下操作:

public class MyModel
{
    public string SomeProperty { get; set; }
}

视图:

@Html.EditorFor(model => model.SomeProperty,"Property")

或者:

型:

public class MyModel
{
    [DataType("Property")]
    public string SomeProperty { get; set; }
}

视图:

@Html.EditorFor(model => model.SomeProperty) 

Views/Shared/EditorTemplates/Property.cshtml:

@model string
@Html.Raw("I'm using property editortemplate:")
@Html.Label(ViewData.ModelMetadata.PropertyName)
@Html.Editor(ViewData.ModelMetadata.PropertyName)
@Html.ValidationMessage(ViewData.ModelMetadata.PropertyName)

最新更新