本文整理 ASP.NET WebForms 時代兩種經典的 **CSS 動態切換/換膚** 實作方式,分別針對「整站主題切換」與「控制項級樣式統一」兩種需求場景。
在 `Page_Load` 建立 `HtmlGenericControl` 代表 ``,加入 `PlaceHolder` (置於 `
`)。<%@ Page Language="C#" %>
<html>
<head runat="server">
<title>動態換膚示範</title>
<asp:PlaceHolder ID="phTheme" runat="server" />
</head>
<body>
<form runat="server">
<asp:DropDownList ID="ddlTheme" runat="server" AutoPostBack="true" OnSelectedIndexChanged="ddlTheme_Changed">
<asp:ListItem Text="預設 (藍)" Value="blue" />
<asp:ListItem Text="深色" Value="dark" />
<asp:ListItem Text="綠意" Value="green" />
</asp:DropDownList>
</form>
</body>
</html>
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
string theme = Session["Theme"]?.ToString() ?? "blue";
ddlTheme.SelectedValue = theme;
RegisterThemeCss(theme);
}
}
protected void ddlTheme_Changed(object sender, EventArgs e)
{
Session["Theme"] = ddlTheme.SelectedValue;
RegisterThemeCss(ddlTheme.SelectedValue);
}
private void RegisterThemeCss(string themeName)
{
// 移除舊的動態 link
var old = phTheme.FindControl("dynamicTheme");
if (old != null) phTheme.Controls.Remove(old);
// 建立新 link
var link = new HtmlGenericControl("link");
link.ID = "dynamicTheme";
link.Attributes["rel"] = "stylesheet";
link.Attributes["type"] = "text/css";
link.Attributes["href"] = ResolveUrl($"~/Themes/{themeName}.css");
phTheme.Controls.Add(link);
}
body { background:#e8f4fd; color:#333; font-family:'微軟正黑體'; }
.btn { background:#007bff; color:#fff; border:none; padding:6px 12px; }
.grid { border-collapse:collapse; width:100%; }
.grid th { background:#007bff; color:#fff; }
.grid td { border:1px solid #ccc; padding:8px; }
適合**快速將舊專案控制項納入 Bootstrap/Tailwind** 等 CSS Framework,不需逐一修改 .aspx 標記。
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack) ApplySkin(Page.Controls);
}
private void ApplySkin(ControlCollection controls)
{
foreach (Control ctrl in controls)
{
switch (ctrl.GetType().Name)
{
case "TextBox":
((TextBox)ctrl).CssClass = "form-control";
break;
case "Button":
((Button)ctrl).CssClass = "btn btn-primary";
break;
case "DropDownList":
((DropDownList)ctrl).CssClass = "form-select";
break;
case "GridView":
((GridView)ctrl).CssClass = "table table-striped table-bordered";
break;
case "CheckBoxList":
((CheckBoxList)ctrl).CssClass = "form-check-input";
break;
}
if (ctrl.HasControls()) ApplySkin(ctrl.Controls);
}
}
<asp:TextBox ID="txtName" runat="server" /> <!-- 渲染: class="form-control" -->
<asp:Button ID="btnSubmit" runat="server" Text="送出" /> <!-- class="btn btn-primary" -->
<asp:GridView ID="gvData" runat="server" /> <!-- class="table table-striped table-bordered" -->
| 比較項目 | 方法一:動態 Link | 方法二:統一 CssClass |
|---|---|---|
| 核心目的 | **整站/頁面級主題切換** (配色、版面、字體) | **控制項外觀標準化** (套用 UI Framework) |
| CSS 管理 | 獨立 .css 檔案 per 主題,設計師可維護 | 依賴現有 Framework CSS (Bootstrap.css 等) |
| 切換機制 | Session/DB 記住主題,換頁自動套用 | 每次請求遞迴重設,或配合 ControlAdapter 全域攔截 |
| 效能影響 | 瀏覽器快取獨立 CSS,切換需重新下載 | ViewState 膨脹 (CssClass 存入),無額外請求 |
| 擴充性 | 高:新增主題只加 .css 檔 | 中:新增控制項型別需修改 switch |
| 建議適用專案 | 多主題系統、部落格、後台管理、行銷活動頁 | 舊專案快速 Bootstrap 化、內部管理系統統一風格 |
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
// 1. 載入主題 CSS (方法一)
RegisterThemeCss(Session["Theme"]?.ToString() ?? "blue");
// 2. 統一控制項 CssClass (方法二)
ApplySkin(Page.Controls);
}
}
**最佳實務**:主題 CSS 定義**色彩變數** (CSS Custom Properties) 或 **語意化類別** (`.btn-primary`、`.bg-surface`),控制項統一套用語意類別,主題切換時僅替換變數定義檔,達到**零 ViewState 膨脹、零遞迴開銷**的現代化換膚架構。
方法一與方法二解決不同層次的樣式管理需求。**現代 ASP.NET Core / Blazor** 應優先採用 **CSS Custom Properties + ``** 或 **Tailwind `dark:` 模式**,配合 `_Layout.cshtml` 統一注入,徹底拋棄伺服器端遞迴設定 `CssClass` 的傳統做法。