• 继续不走寻常路:ASP.NET MVC中使用Web Forms用户控件


    目前我们正在用ASP.NET MVC(Razor)开发新版博客后台,在开发中遇到一个棘手的问题:如何在ASP.NET MVC中使用第三方开发的Web Forms用户控件,比如CuteEditor。

    如果是商业软件,你无法用ASP.NET MVC进行重写;即使是开源软件,你也不可能花时间去重写。你只要两个选择:要么搞定这个问题?要么放弃使用ASP.NET MVC?

    搞软件开发,一个吸引人的地方就是“一切皆有可能”,对于面临的技术问题,只要下定决心去解决,通常都能找到解决方法。

    对于这个问题,我们的思路是:Web Forms用户控件最终输出的就是一段包含HTML代码的字符串,只要拿到这个字符串,通过控制器将字符串传给视图,就能解决问题。

    有了这个思路,我们首先要解决的就是在控制器中得到用户控件的输出字符串,这不是难题,可以用三架马车(StringBuilder+StringWriter+HtmlTextWriter)搞定,代码如下:

    string controlOutput = string.Empty;
    BlogEditor editor
    = new CuteEditorForCNBlogs();
    StringBuilder sb
    = new StringBuilder();
    using (StringWriter sw = new StringWriter(sb))
    {
    using (HtmlTextWriter htw = new HtmlTextWriter(sw))
    {
    editor.RenderControl(htw);
    controlOutput
    = sb.ToString();
    }
    }

    然后,通过ViewBag传递给视图,代码如下:

    ViewBag.EditorHtml = controlOutput;

    最后,在视图中显示一下就搞定(只搞定了部分Web Forms控件),代码如下:

    @{
    Layout = "~/Views/Admin/_AdminLayout.cshtml";
    }
    <div id="editor">
    <div id="editor_main">
    <div>标题</div>
    <input type="text" id="txtTitle" value="" />
    <div>内容</div>
    @Html.Raw(ViewBag.EditorHtml)
    <div><input type="button" value="发布"/></div>
    </div>
    </div>

    到这步,原以为大功告成...在测试CuteEditor控件时,发现革命尚需努力,CuteEditor未能正常输出。

    通过错误信息发现CuteEditor调用了this.Page.Request,而实际Page的值为null。

    于是,我们创建了一个Page的实例,通过Controls.Add将CuteEditor加载到Page实例中,代码如下:

    Page page = new Page();
    string controlOutput = string.Empty;
    BlogEditor editor
    = new CuteEditorForCNBlogs();
    page.Controls.Add(editor);
    ...

    增加Page实例之后,发现Page.Request为null,而且Page.Request为只读属性,无法在创建Page的实例之后进行赋值。

    找遍Page的方法与属性,都没有找到为Page.Request赋值的办法。

    剩下的唯一的希望就是借助Reflector深入“虎穴”,功夫不负有心人,终于发现“虎子”—— SetIntrinsics(HttpContext context):

    private void SetIntrinsics(HttpContext context)
    {
    this.SetIntrinsics(context, false);
    }
    private void SetIntrinsics(HttpContext context, bool allowAsync)
    {
    this._request = context.Request;
    }

    可是,SetIntrinsics是私有方法, 仅供内部使用,咋办?用“反射”挖地道呗。挖地道秘方如下:

    Page page = new Page();
    page.GetType().InvokeMember(
    "SetIntrinsics", BindingFlags.NonPublic |
    BindingFlags.Instance
    | BindingFlags.InvokeMethod, null, page,
    new object[] { System.Web.HttpContext.Current });

    地道挖好,就大功告成,ASP.NET MVC控制器中的完整代码:

    public ActionResult NewPost()
    {
    Page page
    = new Page();
    page.GetType().InvokeMember(
    "SetIntrinsics", BindingFlags.NonPublic |
    BindingFlags.Instance
    | BindingFlags.InvokeMethod, null, page,
    new object[] { System.Web.HttpContext.Current });
    string controlOutput = string.Empty;
    BlogEditor editor
    = new CuteEditorForCNBlogs();
    page.Controls.Add(editor);
    StringBuilder sb
    = new StringBuilder();
    using (StringWriter sw = new StringWriter(sb))
    {
    using (HtmlTextWriter htw = new HtmlTextWriter(sw))
    {
    editor.RenderControl(htw);
    controlOutput
    = sb.ToString();
    }
    }
    ViewBag.EditorHtml
    = controlOutput;
    return View("EditPost");
    }

    继续ASP.NET MVC之旅...

    更新:

    根据Ivony...的建议,使用Server.Execute更简单,代码如下:

    public ActionResult NewPost()
    {
    Page page
    = new Page();
    string controlOutput = string.Empty;
    BlogEditor editor
    = new CuteEditorForCNBlogs();
    page.Controls.Add(editor);
    StringBuilder sb
    = new StringBuilder();
    using (StringWriter sw = new StringWriter(sb))
    {
    using (HtmlTextWriter htw = new HtmlTextWriter(sw))
    {
    Server.Execute(page, htw,
    false);
    controlOutput
    = sb.ToString();
    }
    }
    ViewBag.EditorHtml
    = controlOutput;
    return View("EditPost");
    }

  • 相关阅读:
    机器学习之决策树与随机森林模型
    深度学习入门篇--手把手教你用 TensorFlow 训练模型
    Android 性能测试之方向与框架篇
    机器学习:从入门到第一个模型
    5分钟教你玩转 sklearn 机器学习(上)
    Hbase 技术细节笔记(上)
    五年 Web 开发者 star 的 github 整理说明
    腾讯云发布第三代云服务器矩阵,开放更强计算力赋能产业智能化
    为什么要用深度学习来做个性化推荐 CTR 预估
    云 MongoDB 优化让 LBS 服务性能提升十倍
  • 原文地址:https://www.cnblogs.com/dudu/p/asp_net_mvc_user_control.html
Copyright © 2020-2023  润新知