我为WebFormViewEngine视图编写了一个非常漂亮的菜单Html帮助器。该引擎允许您的助手返回void,并且仍然可以使用:
@Html.Theseus
这对我的助手很有用,因为它可以使用HtmlTextWriter渲染菜单,直接渲染到输出流。
然而,在Razor视图中,Html助手应该返回一个值(通常是MvcHtmlString),这是添加到输出中的值。差异小,后果大。
有一种解决方法,正如GvS(见ASP.NET MVC 2 to MVC 3: Custom Html Helpers in Razor)所指出的那样:
如果助手返回void,请执行以下操作:
@{Html.Theseus;}
(基本上,你只是调用方法,而不是渲染到视图中)。
虽然仍然很整洁,但这与@ Html.seus不完全相同。所以......
我的代码很复杂但效果很好,所以不愿意进行主要编辑,即用另一个编写器替换HtmlTextWriter。代码片段如下:
writer.AddAttribute(HtmlTextWriterAttribute.Href, n.Url);
writer.AddAttribute(HtmlTextWriterAttribute.Title, n.Description);
writer.RenderBeginTag(HtmlTextWriterTag.A);
writer.WriteEncodedText(n.Title);
writer.RenderEndTag();
// Recursion, if any
// Snip off the recursion at this level if specified by depth
// Use a negative value for depth if you want to render the entire sitemap from the starting node
if ((currentDepth < depth) || (depth < 0))
{
if (hasChildNodes)
{
// Recursive building starts here
// Open new ul tag for the child nodes
// "<ul class='ChildNodesContainer {0} Level{1}'>";
writer.AddAttribute(HtmlTextWriterAttribute.Class, "Level" + currentDepth.ToString());
writer.RenderBeginTag(HtmlTextWriterTag.Ul);
// BuildMenuLevel calls itself here to
// recursively traverse the sitemap hierarchy,
// building the menu as I go.
// Note: this is where I increase the currentDepth variable!
BuildChildMenu(currentDepth + 1, depth, n, writer);
// Close ul tag for the child nodes
writer.RenderEndTag();
}
}
使用TagBuilders重写是不会有趣的。就目前而言,它呈现任何类型的菜单,包括我的4guysfromrolla文章中描述的“增量导航”: Implementing Incremental Navigation with ASP.NET
我想我可以返回一个空的MvcHtmlString,但这几乎就是黑客的定义......
唯一的选择是进入夕阳并使用TagBuilder重写帮助程序来构建每个标记,将其添加到StringBuilder,然后构建下一个标记等,然后使用StringBuilder实例创建MvcHtmlString。严重丑陋,除非我能做点像......
有办法:
将HtmlTextWriter渲染停止到流,而是像StringBuilder一样使用它,在我用来创建MvcHtmlString(或HtmlString)的过程结束时?
听起来不太可能,即使我写作......
关于HtmlTextWriter的好处是你可以构建大量的标签,而不是像TagBuilder一样逐个构建它们。
答案 0 :(得分:7)
与您收到的other question Razor的回复相反,您不需要返回HtmlString。您的代码现在的问题是您正在将直接写入响应流。 Razor从内到外执行内容,这意味着你可以搞砸响应顺序(参见similar question)。
所以在你的情况下你可能会这样做(虽然我还没有测试过):
public static void Theseus(this HtmlHelper html)
{
var writer = new HtmlTextWriter(html.ViewContext.Writer);
...
}
修改(跟进以解决您的意见):
Html Helpers完全能够直接返回HtmlString或void并写入上下文编写器。例如,Html.Partial
和Html.RenderPartial
都可以在Razor中正常工作。我认为你困惑的是调用一个版本而不是另一个版本所需的语法。
例如,考虑一个Aspx视图:
<%: Html.Partial("Name") %>
<% Html.RenderPartial("Name") %>
您以不同方式调用每种方法。如果你翻转东西,事情将无法奏效。同样在Razor:
@Html.Partial("Name")
@{ Html.RenderPartial("Name"); }
现在碰巧使用void helper的语法在Razor中比Aspx更冗长。但是,两者都工作得很好。除非你的意思是“问题是html助手无法返回void”。
顺便说一句,如果你真的想用这种语法调用你的助手:@Html.Theseus()
你可以这样做:
public static IHtmlString Theseus(this HtmlHelper html)
{
var writer = new HtmlTextWriter(html.ViewContext.Writer);
...
return new HtmlString("");
}
但这有点像黑客。