【问题标题】:Razor base type / Templated Razor Delegates with the "using" keywordRazor 基本类型/带有“using”关键字的模板化 Razor 委托
【发布时间】:2011-04-13 12:27:58
【问题描述】:

我目前有一个div 容器用于我的表单中的所有输入字段,类似于:

<div class="ux-single-field ui-widget-content ui-corner-all">
  @Html.LabelFor(m => m.Name)
  @Html.TextBoxFor(m => m.Name)
</div>

我想知道如何使用templated razor delegate(或any other trick)封装它,就像我们使用的那样:

@using (Html.BeginForm()) {
}

我可以像这样简单地包装我的元素:

@using (Html.ContentField()) {
  @Html.LabelFor(m => m.Name)
  @Html.TextBoxFor(m => m.Name)
}

【问题讨论】:

  • 请问您要达到什么目的? - 你想让 Html.ContentField() 生成一个 div 吗?
  • @ebb 是的。上面的div。

标签: asp.net asp.net-mvc razor


【解决方案1】:

使用 Razor 视图引擎,工作原理如下:

namespace MyProject.Web.Helpers.Extensions
{
    public static class LayoutExtensions
    {
        public static ContentField BeginContentField(this HtmlHelper htmlHelper)
        {
            return FormHelper(htmlHelper, new RouteValueDictionary());
        }

        public static ContentField BeginContentField(this HtmlHelper htmlHelper, RouteValueDictionary htmlAttributes)
        {
            return FormHelper(htmlHelper, htmlAttributes);
        }

        public static void EndContentField(this HtmlHelper htmlHelper)
        {
            htmlHelper.ViewContext.Writer.Write("</div>");
        }

        private static ContentField FormHelper(this HtmlHelper htmlHelper, IDictionary<string, object> htmlAttributes)
        {
            TagBuilder tagBuilder = new TagBuilder("div");
            tagBuilder.MergeAttributes(htmlAttributes);
            tagBuilder.MergeAttribute("class", "ux-single-field ui-widget-content ui-corner-all");

            htmlHelper.ViewContext.Writer.Write(tagBuilder.ToString(TagRenderMode.StartTag));
            return new ContentField(htmlHelper.ViewContext.Writer);
        }
    }

    public class ContentField : IDisposable
    {
        private bool _disposed;
        private readonly TextWriter _writer;

        public ContentField(TextWriter writer)
        {
            if (writer == null)
                throw new ArgumentNullException("writer");

            _writer = writer;
        }

        [SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase")]
        public void Dispose()
        {
            Dispose(true /* disposing */);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                _disposed = true;

                _writer.Write("</div>");
            }
        }

        public void EndForm()
        {
            Dispose(true);
        }
    }
}

仅供参考:使用旧的 ASPX 引擎,here's how to do it

【讨论】:

    【解决方案2】:

    接受的答案非常有帮助。我为我的项目进行了一些更改和更新,我觉得这个版本对于那些只想跳进去完成工作的人来说会更加清晰。

    变化包括:

    • 已升级为使用匿名类型而不是 IDictionary,因为这似乎是现在的标准。
    • 删除了 Begin/End... 语法,因为我只会将它与 using() 语法一起使用,为此我觉得这更清楚。
    • 为了清晰起见调整了命名。
    • 添加了 headerText 参数,我的面板使用它来创建单独的 div 标题。如果您不需要/不想要它,这很容易删除。
    • 重构了一些方法。
    • 如果您碰巧正在为 KendoUI 寻找面板助手——好吧,这恰好就是这个。我有一个名为 panel 的类,它引用它,它只是为 KendoUI 标签添加边距和宽度。

      使用系统; 使用 System.Diagnostics.CodeAnalysis; 使用 System.IO; 使用 System.Web.Mvc;

      命名空间 MyProject.Web.HtmlHelpers.Extensions { 公共静态类 LayoutExtensions { 公共静态 StyledPanel 面板(此 HtmlHelper htmlHelper,对象 htmlAttributes = null,字符串 headerText = null) { 返回 GetStyledPanel(htmlHelper, headerText, htmlAttributes); }

          private static StyledPanel GetStyledPanel(this HtmlHelper htmlHelper, string headerText, object htmlAttributes)
          {
      
              if (!string.IsNullOrWhiteSpace(headerText))
                  RenderHeading(htmlHelper, headerText);
      
              RenderDiv(htmlHelper, htmlAttributes);
      
              return new StyledPanel(htmlHelper.ViewContext.Writer);
          }
      
          private static void RenderHeading(HtmlHelper htmlHelper, string headerText)
          {
              TagBuilder tagBuilder = new TagBuilder("div");
              tagBuilder.Attributes.Add("class", "panelHead");
              tagBuilder.SetInnerText(headerText);
      
              htmlHelper.ViewContext.Writer.Write(tagBuilder.ToString(TagRenderMode.Normal));
          }
      
          private static void RenderDiv(HtmlHelper htmlHelper, object htmlAttributes) 
          {
              TagBuilder Tag = new TagBuilder("div");
              Tag.MergeAttributes(HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
              Tag.MergeAttribute("class", "panel k-block k-shadow");
      
              htmlHelper.ViewContext.Writer.Write(Tag.ToString(TagRenderMode.StartTag));        
          }
      
      }
      
      public class StyledPanel : IDisposable
      {
          private bool m_Disposed;
          private readonly TextWriter m_Writer;
      
          public StyledPanel(TextWriter writer)
          {
              if (writer == null)
                  throw new ArgumentNullException("Writer was null. This should never happen.");
      
              m_Writer = writer;
          }
      
          [SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase")]
          public void Dispose()
          {
              Dispose(true);
              GC.SuppressFinalize(this);
          }
      
          protected virtual void Dispose(bool disposing)
          {
              if (!m_Disposed)
              {
                  m_Disposed = true;
                  m_Writer.Write("</div>");
              }
          }
      
          public void EndForm()
          {
              Dispose(true);
          }
      }
      

      }

    用法可能是这样的:

    @using (Html.Panel(headerText: "My Header", 
                       htmlAttributes: new { style = "width: 800px;" }))
    { 
        <table>
                <tr>
                    <td class="label">First Name:</td>
                    <td class="content"><input name="thing" class="k-textbox" /></td>
    
                    <td class="label">Last Name:</td>
                    <td class="content"><input name="thing" class="k-textbox" /></td>
                </tr>
        </table>
    }
    

    或者只是:

    @using (Html.Panel())
    { 
        <table>
                <tr>
                    <td class="label">First Name:</td>
                    <td class="content"><input name="thing" class="k-textbox" /></td>
    
                    <td class="label">Last Name:</td>
                    <td class="content"><input name="thing" class="k-textbox" /></td>
                </tr>
        </table>
    }
    

    【讨论】:

      【解决方案3】:

      让我们看看(或猜测)Html.BeginForm() 做了什么。从“渲染的角度”来看,它通常只是将开始表单标签渲染到 html 输出中。它是一次性的,因为在这种情况下,它知道表单的内部 html 内容何时完成渲染,并且可以在其 Dispose() 方法中渲染结束 &lt;/form&gt; 标记。有了所有这些,您将得到 - 首先,呈现打开的 form 标记,而不是您希望的自定义​​ html 内容,然后是结束标记。结果 - 您在输出中获得完整的 html 表单。

      <form>
      
      ...contents(Result of Html.TextBoxFor, etc. helpers)
      
      </form>
      

      我认为您的情况最好像形式一样解决。目前我没有太多时间编写完整的代码,但是如果你通过查看源代码来查看FormExtensions.BeginForm(谢谢@druttka)反射器(如果你有旧版本或购买了许可证)或http://wiki.sharpdevelop.net/ilspy.ashx 和上面的解释,你可以从哪里开始。从 BeginForm 方法中删除不必要的代码,创建您的 MvcContentField : IDisposable 类而不是 MvcForm,更改其上的 Dispose() 以呈现结束 div 标记,您将得到您所需要的。

      【讨论】:

      • 不需要Reflector或ILSpy; MVC3 源代码可免费获得aspnet.codeplex.com/releases/view/58781 检查 Html 文件夹/命名空间下的 MvcForm.cs 和 FormExtensions.cs 文件。
      • 嗯。我会调查并尝试弄清楚。会及时通知您。
      • @druttka 我只是匆忙忘记了它。谢谢:)
      猜你喜欢
      • 2011-07-05
      • 2012-06-14
      • 2011-07-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-02-13
      • 1970-01-01
      相关资源
      最近更新 更多