继续不走寻常路: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");
}
复制代码

posted @   dudu  阅读(9193)  评论(19编辑  收藏  举报
编辑推荐:
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· PostgreSQL 和 SQL Server 在统计信息维护中的关键差异
阅读排行:
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· spring官宣接入deepseek,真的太香了~
历史上的今天:
2004-03-19 功能调整:阅读排行、回复排行
2004-03-19 DataSet.WriteXml(String)与DataSet.WriteXml(Stream)的区别
点击右上角即可分享
微信分享提示