問題背景
博客園博客中的日歷用的是ASP.NET WebForms的日歷控件(System.Web.UI.WebControls.Calendar),它會為“上一月”、“下一月”的鏈接生成"__doPostBack()"的js調用,如下圖:
目前發現它會帶來兩個問題:
1. 不支持IE10;
2. 某些電腦不允許執行__doPostBack。
問題提煉
前提:
- 我們想以最低的成本解決這個問題,也就是對當前代碼盡可能少的改動。所以要盡可能重用現有的日歷控件代碼。
- 日歷改為Ajax加載,點擊“上一月”、“下一月”時Ajax更新日歷內容。
- 用ASP.NET MVC處理Ajax請求。
要解決的問題:
如何在ASP.NET MVC Controller中加載包含WebForms日歷控件的用戶控件(.ascx),並得到其輸出的字符串,然后將__doPostBack的代碼替換為ajax調用代碼。
核心問題:
如何在ASP.NET MVC Controller中得到用戶控件(.ascx)輸出的字符串。
解決方法
先看代碼
public ActionResult Calendar() { var page = new Page(); var form = new HtmlForm(); var calendar = page.LoadControl("~/Controls/CNBlogsCalendar.ascx"); form.Controls.Add(calendar); page.Controls.Add(form); using (var sw = new StringWriter()) { System.Web.HttpContext.Current.Server.Execute(page, sw, true); return Content(sw.ToString()); } }
代碼很簡單,但得到這個代碼花了今天一上午時間。
代碼說明:
- 必須要new Page(),只有Page才能LoadControl。
- 必須要new HtmlForm(),因為日歷控件必要要放在<form runat="server">之間。
- 關鍵功臣是HttpContext.Current.Server.Execute,動態加載控件並輸出字符串全靠它。這個功臣是在這篇文章中找到的(感謝Sam Mueller)。之前我用過的方法(繼續不走尋常路:ASP.NET MVC中使用Web Forms用戶控件)不僅麻煩,而且在這個場景下會有問題。
代碼運行結果:
完整代碼下載: