WebForms 和 MVC 和谐共处 — 差不多…






4.67/5 (2投票s)
不能在 ASP.NET MVC 中使用 WebControls 了吗? 不完全是。 这篇文章讨论了你在 MVC 中创建 "WebControls" 的一些选择,通过使用扩展方法,IDisposable 或一个 "超级秘密" 的方法。
已更新的内容可用
查看我的新系列,关于在 ASP.NET MVC 视图页面中使用 WebControls。
快速测验 – 以下 ASP.NET 代码片段会发生什么?
<% int number = 5; %>
<asp:PlaceHolder runat="server" >
<% =number %>
</asp:PlaceHolder>
打印数字 5? 不是。 也许它等于零? 有点。 那么这个呢…
Compiler Error Message: CS0103: The name ‘number’ does not exist in the current context
哇哦…
我之前抱怨过WebControls 和实际内联代码之间的脱节。 WebControls 仍然是一种非常方便的编写模板的方式,但由于它们存在于与内联代码不同的上下文中,因此它们实际上是禁止的。 就像 MVC 很酷一样,你几乎只能将所有现有的 WebControls 扔出窗外。 还是可以保留一些的?
使用扩展方法代替控件
扩展方法真的在最好的时间出现了。 我看不出没有它们 MVC 怎么能工作。
如果你以前从未使用过扩展方法,扩展方法允许你在项目的其他地方创建一个 static
方法,进行一些奇特的赋值,然后将其附加到你目标类上。 LINQ 严重依赖扩展方法来提供如此无缝的编程体验。
ASP.NET MVC 使用扩展方法的一种方式是使使用某些控件类型更容易。 例如,有一种方法可以创建 input
标签,一种方法可以渲染 form
标签等等。
下面是一个示例,说明如何创建附加到 HtmlHelper
的扩展方法。
public static class MyExtensionMethods {
//example method - don't write things this ugly
public static string BulletList(this HtmlHelper helper, params string[] items) {
return string.Concat(
"<ul><li>",
string.Join("</li><li>", items),
"</li></ul>"
);
}
}
在我们的示例中,我们创建了一个 static
类来容纳我们的扩展方法。 我们还创建了以奇怪的参数开头的 static
方法。 这个参数实际上是我们附加方法到的类。 现在我们可以像这样使用我们的代码。
<% =this.Html.BulletList("Apple", "Orange", "Pear") %>
酷。 如果你不熟悉可以使用扩展方法做的事情,那么我建议你在开始尝试将它们添加到你的项目之前,更多地阅读它们。 你还可以使用委托来模拟扩展方法中的模板。
public static class MyExtensionMethods {
//example method - renders the content of each action
public static void TwoColumns(this HtmlHelper helper, Action left, Action right) {
HttpContext.Current.Response.Write("<div class='left'>");
left();
HttpContext.Current.Response.Write("</div>");
HttpContext.Current.Response.Write("<div class='right'>");
right();
HttpContext.Current.Response.Write("</div>");
}
}
然后你可以像这样使用你的“模板”…
<% this.Html.TwoColumns(() => { /* Left Column */ %>
I'm on the left!
<% }, () => { /* Right Column */ %>
I'm on the right!
<% }); /* End Two Column */ %>
这样的代码很快就会变得难看 – 所以在使用时要保守。
使用 IDisposable 关闭标签
你可以使用 ASP.NET MVC 创建 "WebControl
" 的另一种方法是创建一个实现 IDisposable
的类。 通过将标记放在构造函数和 Dispose
方法中,你基本上可以编写你通常在 CompositeControls
上找到的 RenderBeginTag()
和 RenderEndTag()
方法!
public class StyledHeader : IDisposable {
public StyledHeader(string color) {
HttpContext.Current.Response.Write("<h1 style='color:" + color + "' >");
}
public void Dispose() {
HttpContext.Current.Response.Write("</h1>");
}
}
自然地,StyledHeader
应该添加到 ASP.NET MVC 库的核心,但不知何故它错过了 。 在任何情况下,我们的类都可以与
using
关键字一起使用,以渲染我们花哨的新标题。
<% using (new StyledHeader("#f00")) { %>
Howdy - This is my header control!
<% } /* End StyledHeader */ %>
超级秘密的最终方法
正如你在我的帖子开头注意到的,我提到过扔掉 WebControls
,因为它们对我们没有任何用处了。 嗯,这不是真的 — 我们仍然可以将 WebControl
与页面的内联代码一起使用!
如果你阅读过我之前的任何博客文章,你可以看到我非常喜欢覆盖 WebControls 的 Render()
方法。 以类似的方式,我们将使用 RenderControl()
方法在我们需要它们时立即渲染我们的 WebControl
s。
using System.Reflection;
using System.IO;
using System.Web.UI;
using System.Web;
using System.Web.Mvc;
public static class MyExtensionMethods {
//example method - renders a webcontrol to the page
public static void RenderControl(this HtmlHelper helper, Control control) {
//perform databinding if needed
MethodInfo bind = control.GetType().GetMethod("DataBind");
if (bind is System.Reflection.MethodInfo) {
bind.Invoke(control, null);
}
//render the HTML for this control
StringWriter writer = new StringWriter();
HtmlTextWriter html = new HtmlTextWriter(writer);
control.RenderControl(html);
//write the output
HttpContext.Current.Response.Write(writer.ToString());
//and cleanup the writers
html.Dispose();
writer.Dispose();
}
}
你可能会注意到我们正在进行的礼貌的 DataBind()
调用 — 以防万一有 DataSource
,我也在调用该方法。 根据你如何使用它,你可能需要稍微改变一下。 但说够了,它是如何使用的?
<% int[] numbers = { 1, 2, 3, 4, 5 }; %>
<% this.Html.RenderControl(new DataGrid() { DataSource = numbers }); %>
你也可以在你将类传递到 RenderControl
方法之前定义你的类,以防你需要做的不仅仅是将一些值分配给属性。
最后,WebForms 和 MVC 和谐共处… 或者可能不是…
现在,我不会假装你可以将你的所有 WebControl
s 插入其中并期望它像 WebForms 过去一样工作。 很多 WebControl
s 依赖的很多东西都缺失了(比如 ViewState
)。 但是,如果你主要对 WebControl
的渲染输出感兴趣,那么你很幸运。