<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="UpdatePanelDemo.aspx.cs" Inherits="Demo.UpdatePanelDemo" %>
<html xmlns="https://www.w3.org/1999/xhtml">
<head runat="server">
<title>UpdatePanel 無刷新示例</title>
</head>
<body>
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server" />
<h2>UpdatePanel 局部更新演示</h2>
<asp:UpdatePanel ID="UpdatePanel1" runat="server"
UpdateMode="Conditional" RenderMode="Block">
<ContentTemplate>
<div style="padding: 20px; border: 1px solid #ddd; background: #f9f9f9;">
<strong>伺服器時間:</strong>
<asp:TextBox ID="txtTime" runat="server" ReadOnly="true" Width="200px" />
<br /><br />
<asp:Button ID="btnGetTime" runat="server" Text="獲取伺服器時間" OnClick="btnGetTime_Click" CssClass="btn btn-primary" />
<asp:Button ID="btnFullPostback" runat="server" Text="完整回發對比" OnClick="btnFullPostback_Click" CssClass="btn btn-secondary" />
</div>
</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="btnGetTime" EventName="Click" />
<!-- btnFullPostback 故意不加觸發器,將執行完整回發 -->
</Triggers>
</asp:UpdatePanel>
<hr />
<div>
<strong>頁面載入時間:</strong> <asp:Label ID="lblPageLoad" runat="server" />
</div>
</form>
</body>
</html>
public partial class UpdatePanelDemo : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
lblPageLoad.Text = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
}
}
protected void btnGetTime_Click(object sender, EventArgs e)
{
// 模擬耗時操作
System.Threading.Thread.Sleep(500);
txtTime.Text = DateTime.Now.ToString("HH:mm:ss.fff");
// 僅更新 UpdatePanel 區域,lblPageLoad 不會變化
}
protected void btnFullPostback_Click(object sender, EventArgs e)
{
// 完整回發:整頁重新渲染,lblPageLoad 會更新
System.Threading.Thread.Sleep(500);
txtTime.Text = DateTime.Now.ToString("HH:mm:ss.fff");
}
}
<asp:UpdatePanel ID="upMain" runat="server" UpdateMode="Conditional">
<ContentTemplate>...</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="btnRefresh" />
<asp:AsyncPostBackTrigger ControlID="ddlFilter" EventName="SelectedIndexChanged" />
</Triggers>
</asp:UpdatePanel>
配合後端手動觸發:`upMain.Update();`
<asp:UpdateProgress ID="UpdateProgress1" runat="server" AssociatedUpdatePanelID="UpdatePanel1">
<ProgressTemplate>
<div class="loading-overlay">
<div class="spinner">載入中...</div>
</div>
</ProgressTemplate>
</asp:UpdateProgress>
<style>
.loading-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(255,255,255,0.8); z-index: 9999; display: flex; align-items: center; justify-content: center; }
.spinner { padding: 20px 40px; background: #333; color: white; border-radius: 4px; font-size: 16px; }
</style>
<asp:ScriptManager ID="ScriptManager1" runat="server"
OnAsyncPostBackError="ScriptManager1_AsyncPostBackError" />
// 後端
protected void ScriptManager1_AsyncPostBackError(object sender, AsyncPostBackErrorEventArgs e)
{
ScriptManager1.AsyncPostBackErrorMessage = $"操作失敗:{e.Exception.Message}";
// 記錄完整異常到日誌
LogException(e.Exception);
}
// 前端處理
// 頁面載入/部分更新完成後執行
| 問題 | 原因 | 解決 |
|---|---|---|
| UpdatePanel 不更新 | UpdateMode=Conditional 但無 Trigger/手動 Update() | 添加 AsyncPostBackTrigger 或呼叫 UpdatePanel.Update() |
| 頁面閃爍/完整回發 | 按鈕在 UpdatePanel 外、驗證控件導致、JS 錯誤中斷 | 移入 Panel、設定 ValidationGroup、檢查 Console |
| 檔案上傳失效 | multipart/form-data 不支援異步 | 使用 PostBackTrigger 或改用 AJAX FormData 上傳 |
| ViewState 膨脹 | 大量資料存 ViewState | 關閉 ViewState、移至 Session/快取、壓縮 |
| jQuery 事件失效 | DOM 重新渲染,原綁定失效 | 使用 `pageLoaded` 重新綁定或事件委託 `$(document).on()` |
UpdatePanel 是 WebForms 時代的產物,以 ViewState 和部分渲染換取開發便利性。理解其 `ScriptManager` 管線、`PageRequestManager` 客戶端生命週期、`UpdateMode` 機制,對維護舊系統至關重要。新專案請毫不猶豫擁抱現代前端架構,將伺服器端渲染限制在首屏 SEO 與初始狀態,後續交互全面前端化。