65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.67/5 (2投票s)

2009年7月7日

CC (ASA 2.5)

3分钟阅读

viewsIcon

17937

不能在 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() 方法在我们需要它们时立即渲染我们的 WebControls。

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 和谐共处… 或者可能不是…

现在,我不会假装你可以将你的所有 WebControls 插入其中并期望它像 WebForms 过去一样工作。 很多 WebControls 依赖的很多东西都缺失了(比如 ViewState)。 但是,如果你主要对 WebControl 的渲染输出感兴趣,那么你很幸运。

© . All rights reserved.