12

通常我通过@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

但它不起作用:无论 DisplayName 是什么都呈现标签,并且根本不呈现编辑器(在 Watches Html.Editor(modelMetadata.PropertyName 显示空字符串)中。我做错了什么?

4

2 回答 2

12

您正在从您的编辑器中调用编辑器。正如@RPM1984 在答案的评论中改写@darin-dmitrov 一样:在给定的 Views specific context 中,您只能在运行时为给定类型使用 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")

而且,关于无论 DisplayName 是否都呈现标签,为了防止标签在没有显示名称的情况下呈现,请将您的 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 等),并且只有当您有一些不是字符串的“未知”属性并且没有特定的编辑器时(即字节 [])它将为其调用 object.ascx ,但这不是为当前属性调用编辑器(您要执行的操作):

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


概括

精简版:

相同属性的更多编辑器基本上是功能差异的解决方案(“对于是/否,我想要这里的无线电组和那里的下拉菜单)”,对于视觉差异,应该使用部分视图,因为你可以尽可能多地嵌套它们,因为您明确地按名称调用它们,因此没有施加任何限制,您有责任防止任何潜在的递归。

长版:

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

此外,如果您可以将当前属性的另一个模板插入到当前模板中,则可以为当前属性链接任意数量的模板,并且很容易导致递归,因此它会以一种或另一种方式引导您进入stackoverflow :)

于 2012-09-01T21:43:01.450 回答
0

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

我的模型.cs

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

属性.cs

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

视图/共享/EditorTemplates/Property.cshtml

@model MvcApplication1.Models.Property

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

我的视图.cshtml

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

如果您没有为 EditorFor 助手提供模板名称,它会找到名称与SomeProperty的type匹配的 editortemplate 。

更新:

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

模型

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) 

视图/共享/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)
于 2012-08-30T10:58:44.150 回答