面對「我的申請」與「待我審批」兩個高度相似頁面:查詢條件、列表顯示、審批歷程完全一致,唯一差異在於「待審批」多一個「執行審批」操作欄,且不同應用/角色顯示欄位不同。利用繼承與多型抽象共同邏輯,配合動態 GridView 欄位實現靈活擴充。
MyApprovals.aspx:我的申請列表ToApprovals.aspx:待我審批列表 (含操作欄)
public abstract class ApprovalBasePage : BasePage {
// 子類決定頁面類型:0=我的申請, 1=待審批
protected virtual int PageType { get { return 0; } }
private ApprovalData _dataSource;
protected ApprovalData DataSource {
get {
if (_dataSource == null)
_dataSource = ApprovalData.GetData(this.PageType);
return _dataSource;
}
}
// 核心資料綁定邏輯 (可被子類覆寫)
protected virtual void BindData(string key = "") {
var dt = DataToTable(key);
if (dt != null) {
gvApprovals.DataSource = dt;
gvApprovals.DataBind();
}
}
// 將 ApprovalData 轉為 DataTable 供 GridView 綁定
protected DataTable DataToTable(string key = "") {
var dt = new DataTable();
if (DataSource == null) return null;
// 動態建立欄位
foreach (var colName in DataSource.HeadList)
dt.Columns.Add(colName, typeof(string));
// 填充資料
foreach (var rowData in DataSource.ContentList) {
var dr = dt.NewRow();
for (int i = 0; i < rowData.Count && i < dt.Columns.Count; i++)
dr[i] = rowData[i];
dt.Rows.Add(dr);
}
return dt;
}
// 共用事件處理
protected void Page_Load(object sender, EventArgs e) {
if (!IsPostBack) BindData();
}
protected void btnSearch_Click(object sender, EventArgs e) {
BindData(txtKey.Text);
}
}
public class ApprovalData {
public List HeadList { get; set; } // 欄位標題
public List> ContentList { get; set; } // 資料列集合
public static ApprovalData GetData(int pageType) {
// 實際專案中從資料庫/服務取得
// 此處模擬資料
var data = new ApprovalData();
data.HeadList = new List { "流水號", "申請人", "金額", "狀態" };
if (pageType == 1) data.HeadList.Add("操作"); // 待審批多一欄
data.ContentList = new List> {
new List { "A001", "張三", "1000", "審核中" },
new List { "A002", "李四", "2000", "已核准" }
};
if (pageType == 1) {
data.ContentList[0].Add("審批");
data.ContentList[1].Add("審批");
}
return data;
}
}
public partial class MyApprovals : ApprovalBasePage {
protected override int PageType { get { return 0; } }
// 可選:覆寫 BindData 增加特定邏輯
// protected override void BindData(string key = "") { ... }
}
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="MyApprovals.aspx.cs" Inherits="WebApp.MyApprovals" MasterPageFile="~/Site.Master" %>
<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">
<p>
<asp:TextBox ID="txtKey" runat="server"></asp:TextBox>
<asp:Button ID="btnSearch" runat="server" Text="查詢" OnClick="btnSearch_Click" />
<asp:Label ID="lblMsg" runat="server" ForeColor="Red"></asp:Label>
</p>
<p>
<asp:GridView ID="gvApprovals" runat="server" AutoGenerateColumns="true"></asp:GridView>
</p>
</asp:Content>
public partial class ToApprovals : ApprovalBasePage {
protected override int PageType { get { return 1; } }
protected override void BindData(string key = "") {
base.BindData(key); // 先綁定基本資料
// 動態添加操作按鈕欄 (若未自動產生)
if (gvApprovals.Columns.Count == 0) { // AutoGenerateColumns=false 時
var btnField = new ButtonField {
HeaderText = "操作",
Text = "審批",
CommandName = "Approve",
ButtonType = ButtonType.Button
};
gvApprovals.Columns.Add(btnField);
}
}
protected void gvApprovals_RowCommand(object sender, GridViewCommandEventArgs e) {
if (e.CommandName == "Approve") {
int index = Convert.ToInt32(e.CommandArgument);
// 執行審批邏輯
}
}
}
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="ToApprovals.aspx.cs" Inherits="WebApp.ToApprovals" MasterPageFile="~/Site.Master" %>
<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">
<p>...查詢區同 MyApprovals...</p>
<p>
<asp:GridView ID="gvApprovals" runat="server" AutoGenerateColumns="false" OnRowCommand="gvApprovals_RowCommand">
<Columns>
<!-- 動態產生 BoundField 對應 HeadList -->
</Columns>
</asp:GridView>
</p>
</asp:Content>
| 模式 | 應用位置 | 效益 |
|---|---|---|
| Template Method | BindData 定義演算法骨架,DataToTable 為可變步驟 | 固定流程、允許局部客製化 |
| Factory Method | PageType 屬性決定資料來源型態 | 延遲具體型別建立到子類 |
| 動態欄位生成 | DataToTable 根據 HeadList 建立 DataTable 欄位 | 支援不同應用/角色顯示不同欄位 |
| 策略模式 (潛在) | 若審批邏輯複雜,可抽出 IApprovalStrategy 注入 | 分離演算法、支援運行時切換 |
<table> 由 foreach 渲染,完全控制 HTML。ViewBag.Columns 或 IColumnProvider 服務配置。透過基類封裝共同邏輯 (查詢、綁定、資料轉換)、虛擬屬性/方法實現多型差異 (頁面類型、操作欄)、動態 DataTable 適配 GridView,成功消除重複代碼,支援欄位動態配置。此範例展示經典 ASP.NET Web Forms 時代的物件導向設計精髓,現代開發應遷移至 MVC/Core 架構並採用前端元件庫。