在 ASP.NET WebForms 時代,前端 JavaScript 常散落在 `.aspx` 頁面、使用者控制項、甚至後台代碼 `RegisterStartupScript` 中,難以維護與複用。本文展示一個完整的伺服器端腳本元件化框架:定義 `IExecuteScript` 介面、實作 `ShowModalDialog` 類別封裝 `window.showModalDialog` 邏輯、搭配 `RegisterScript` 靜態類別註冊客戶端事件,實現「伺服器端配置、客戶端執行」的關注點分離。
IExecuteScript (介面)
└─ ShowModalDialog : 彈出視窗並接收回傳值
├─ 屬性:PageContainer, OpenPage, Height, Width, Parent
├─ 集合:Parameters (傳遞參數), ReturnElements (接收回傳值對應控制項)
└─ Execute() → 產生完整 JavaScript 字串
ReturnElement : 回傳值映射 (控制項 ClientID ↔ Key)
Parameter : 傳遞參數描述 (名稱、值、型別: Element/Select)
RegisterScript : 靜態註冊工具
├─ RegiOnClick(Control, IExecuteScript) : 綁定 onclick
└─ RegiPageClient(Page, key, ReturnValues) : 頁面載入註冊回傳處理
public interface IExecuteScript { string Execute(); }
public class ShowModalDialog : IExecuteScript
{
private const string DialogTemplate =
"window.showModalDialog({0}, ' ', 'dialogHeight:{1}px;dialogWidth:{2}px;...');";
public string PageContainer { get; set; } // 容器頁 URL
public string OpenPage { get; set; } // 目標頁 URL
public int Height { get; set; } = 400;
public int Width { get; set; } = 400;
public bool Parent { get; set; } // 是否使用 parent.
public ReturnElementCollection ReturnElements { get; } = new ReturnElementCollection();
public ParameterCollection Parameters { get; } = new ParameterCollection();
public string Execute()
{
string url = $"'{PageContainer}?pageurl=' + {GetPage()}";
url = string.Format(DialogTemplate, url, Height, Width);
url = GetParent() + url;
if (ReturnElements.Count > 0)
{
url = "var getvalue=" + url + ";if(getvalue != null){";
foreach (ReturnElement item in ReturnElements)
url += item.GetScript(GetParent());
url += "}";
}
return url + ";";
}
private string GetPage()
{
if (Parameters.Count == 0) return $"'{OpenPage}'";
var sb = new StringBuilder($"'{OpenPage}'");
for (int i = 0; i < Parameters.Count; i++)
{
var p = Parameters[i];
string param = p.Element == ElementType.Element
? $"'{p.Name}=' + {GetParent()}document.all('{p.Value}').value"
: $"'{p.Name}=' + {GetParent()}__getSeletedButton({GetParent()}document.all('{p.Value}'))";
sb.Append(i == 0 ? $" + '{HttpUtility.UrlEncode("?")}' + {param}"
: $" + '{HttpUtility.UrlEncode("&")}' + {param}");
}
return sb.ToString();
}
// ... GetParent() 等輔助方法
}
var dialog = new ShowModalDialog(
WebBoot + "Appunit/WindowOpenDialog.aspx",
FrmFailureLogSearch.PageURI());
dialog.Height = 400;
dialog.Width = 600;
// 指定回傳值填入哪個控制項
dialog.ReturnElements.Add(new ReturnElement(txtReturn.ClientID, "ReturnString"));
// 註冊到按鈕點擊事件
RegisterScript.RegiOnClick(cmdSearch, dialog);
var returnValue = new ReturnValues();
returnValue.Add("ReturnString", filter); // filter 為查詢條件字串
RegisterScript.RegiPageClient(this, "return", returnValue);
| 特性 | 說明 |
|---|---|
| 強型別配置 | 編譯期檢查屬性拼寫、型別錯誤 |
| 參數化 URL 建構 | 自動處理 QueryString 編碼、多參數串接 |
| 回傳值聲明式映射 | 伺服器端指定 `ControlID` ↔ `Key`,客戶端自動填入 |
| 支援多種參數來源 | `Element` (輸入框值)、`Select` (RadioButtonList 選中值) |
| 可擴充新腳本類型 | 實作 `IExecuteScript` 即可:`ConfirmDialog`、`AjaxPost`、`PrintPreview` 等 |
此框架是 WebForms 時代「伺服器端主導客戶端行為」的經典產物,雖技術棧已老舊,但其**元件化封裝、聲明式配置、關注點分離**的設計思想,在任何時代的 UI 開發中都值得借鑑。