在 ASP.NET 開發中,常需將伺服器控制項(含整個 `.aspx` 頁面)渲染為 HTML 字串,用於:郵件內容產生、PDF 匯出、XSLT 轉換、快取片段、記錄測試輸出等。本文提供通用的 `Render` 工具類(C# / VB.NET 雙版本),封裝 `HtmlTextWriter` + `StringWriter` 模式,三種多載涵蓋所有常見場景。
ASP.NET 控制項透過 `RenderControl(HtmlTextWriter)` 輸出 HTML。將 `HtmlTextWriter` 包裝在 `StringWriter` 上,即可捕獲輸出字串:
var sb = new StringBuilder();
var sw = new StringWriter(sb);
var hw = new HtmlTextWriter(sw);
control.RenderControl(hw);
return sb.ToString();
using System;
using System.IO;
using System.Text;
using System.Web;
using System.Web.UI;
public class Render
{
/// 渲染任意 Control 為字串
public static string RenderControl(Control control)
{
var sb = new StringBuilder(1024);
control.RenderControl(new HtmlTextWriter(new StringWriter(sb)));
return sb.ToString();
}
/// 渲染 TemplateControl (Page/UserControl) 為字串
public static string RenderControl(TemplateControl control)
{
var sb = new StringBuilder(1024);
control.RenderControl(new HtmlTextWriter(new StringWriter(sb)));
return sb.ToString();
}
/// 透過 Server.Execute 執行指定 .aspx 頁面並取得輸出
public static string RenderPage(string pageLocation)
{
var context = HttpContext.Current;
var sb = new StringBuilder(1024);
context.Server.Execute(pageLocation, new HtmlTextWriter(new StringWriter(sb)));
return sb.ToString();
}
}
Imports System
Imports System.IO
Imports System.Text
Imports System.Web
Imports System.Web.UI
Public Class Render
Public Shared Function RenderControl(ByVal control As Control) As String
Dim sb As New StringBuilder(1024)
control.RenderControl(New HtmlTextWriter(New StringWriter(sb)))
Return sb.ToString()
End Function
Public Shared Function RenderControl(ByVal control As TemplateControl) As String
Dim sb As New StringBuilder(1024)
control.RenderControl(New HtmlTextWriter(New StringWriter(sb)))
Return sb.ToString()
End Function
Public Shared Function RenderPage(ByVal pageLocation As String) As String
Dim context As HttpContext = HttpContext.Current
Dim sb As New StringBuilder(1024)
context.Server.Execute(pageLocation, New HtmlTextWriter(New StringWriter(sb)))
Return sb.ToString()
End Function
End Class
var emailBody = Render.RenderControl(pnlEmailTemplate);
// 或指定頁面
var emailBody = Render.RenderPage("~/Templates/Email/OrderConfirm.aspx");
SendMail("user@example.com", "訂單確認", emailBody, true);
string html = Render.RenderControl(myDataGrid);
XslCompiledTransform xslt = new XslCompiledTransform();
xslt.Load("transform.xslt");
using (var output = new StringWriter())
{
xslt.Transform(new XPathDocument(new StringReader(html)), null, output);
string pdfHtml = output.ToString(); // 後續轉 PDF
}
string cachedHtml = Cache["HeaderMenu"] as string;
if (cachedHtml == null)
{
cachedHtml = Render.RenderControl(ucHeaderMenu);
Cache.Insert("HeaderMenu", cachedHtml, null, DateTime.Now.AddMinutes(30), Cache.NoSlidingExpiration);
}
litHeader.Text = cachedHtml;
| 限制 | 說明 | 替代方案 |
|---|---|---|
| 需在 Page 生命週期中執行 | `RenderControl` 需控制項已建立控制項樹 | 在 `Page_Load` 之後、`Page_Render` 之前呼叫 |
| `Server.Execute` 會觸發完整頁面生命週期 | 包含 `Page_Init`、`Page_Load` 等事件 | 確保目標頁面無副作用(如重複寫入 DB) |
| 驗證控制項需 ` | 否則 `RenderControl` 會拋出例外 | 呼叫 `Page.VerifyRenderingInServerForm(control)` |
| 非同步頁面/非同步控制項 | 可能導致死鎖或輸出不完整 | 改用 `HttpContext.Current.Response.Write` 串流輸出 |
此 `Render` 工具類雖屬 WebForms 時代產物,但其核心思想——**捕獲渲染管線輸出**——在現代框架中依然適用,理解其運作機制有助於跨時代技術遷移與整合。