首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> ASP.NET 基礎 >> 關於ASP.NET

ASP.NET 伺服器控件開發基礎 (20):2.0 新版資料綁定控件架構全解析

     編輯:關於ASP.NET

時代變遷:從手動繫結到宣告式繫結

ASP.NET 1.x 時代,資料綁定意味著:grid.DataSource = GetData(); grid.DataBind();。ASP.NET 2.0 引入資料源控件 (SqlDataSource, ObjectDataSource) 後,開發模式徹底改變:


<asp:GridView ID="GridView1" runat="server" DataSourceID="SqlDataSource1" AutoGenerateColumns="False">
    <Columns>
        <asp:BoundField DataField="CompanyName" HeaderText="公司名稱" SortExpression="CompanyName" />
        <asp:BoundField DataField="ContactName" HeaderText="聯絡人" SortExpression="ContactName" />
    </Columns>
</asp:GridView>

<asp:SqlDataSource ID="SqlDataSource1" runat="server"
    ConnectionString="<%$ ConnectionStrings:NorthwindConnectionString %>"
    SelectCommand="SELECT CompanyName, ContactName FROM Customers">
</asp:SqlDataSource>

零程式碼實現資料查詢、分頁、排序、CRUD。但這背後是什麼機制?如何開發自己的新版資料綁定控件?

新版資料綁定控件兩大基類

BaseDataBoundControl 與 DataBoundControl 類別圖

BaseDataBoundControl - 抽象基礎


public abstract class BaseDataBoundControl : Control, IDataBoundControl
{
    // 核心屬性
    public string DataSourceID { get; set; }
    public virtual IEnumerable DataSource { get; set; }
    public string DataMember { get; set; }
    protected bool RequiresDataBinding { get; set; }
    protected DataSourceSelectArguments SelectArguments { get; }
    
    // 核心方法
    protected abstract void PerformDataBinding(IEnumerable data);
    protected virtual void OnDataPropertyChanged() { RequiresDataBinding = true; }
    protected virtual DataSourceView GetDataSourceView() { /* 解析 DataSourceID */ }
    protected virtual IEnumerable GetData() { /* 從 DataSource 或 DataSourceView 取得 */ }
    
    // 生命週期
    protected override void OnPreRender(EventArgs e)
    {
        base.OnPreRender(e);
        if (RequiresDataBinding) EnsureDataBound();
    }
}

DataBoundControl - 功能豐富的具體基類


public abstract class DataBoundControl : BaseDataBoundControl
{
    // 擴展功能:分頁、排序、樣式、模板
    public virtual int PageSize { get; set; }
    public virtual bool AllowPaging { get; set; }
    public virtual bool AllowSorting { get; set; }
    public virtual string SortExpression { get; }
    public virtual SortDirection SortDirection { get; }
    
    // 樣式屬性
    public TableItemStyle HeaderStyle { get; }
    public TableItemStyle FooterStyle { get; }
    public TableItemStyle RowStyle { get; }
    public TableItemStyle AlternatingRowStyle { get; }
    public TableItemStyle EditRowStyle { get; }
    public TableItemStyle SelectedRowStyle { get; }
    public TableItemStyle PagerStyle { get; }
    
    // 模板 (適用於 FormView, DetailsView 等)
    public virtual ITemplate ItemTemplate { get; set; }
    public virtual ITemplate EditItemTemplate { get; set; }
    // ...
}

資料綁定控件與資料源控件的協作機制

核心流程

  1. 識別資料源:透過 DataSourceID 尋找 IDataSource 實作
  2. 取得視圖:呼叫 GetView("") 取得 DataSourceView
  3. 能力協商:檢查視圖支援 CanPage、CanSort、CanRetrieveTotalRowCount
  4. 建立查詢參數:建構 DataSourceSelectArguments (包含分頁、排序資訊)
  5. 執行查詢:呼叫 ExecuteSelect(arguments) 取得 IEnumerable
  6. 繫結資料:呼叫 PerformDataBinding(data) 建立 UI 階層
  7. 自動重新繫結:資料變更時,DataSourceView.OnDataSourceViewChanged 觸發 RequiresDataBinding = true

關鍵介面:DataSourceSelectArguments


public class DataSourceSelectArguments
{
    public int StartRowIndex { get; set; }      // 分頁起始索引
    public int MaximumRows { get; set; }        // 每頁筆數
    public string SortExpression { get; set; }  // 排序運算式
    public bool RetrieveTotalRowCount { get; set; } // 是否需要總筆數
    
    // 能力檢查:若視圖不支援分頁但要求分頁,拋出異常
    public void RaiseUnsupportedCapabilitiesError(DataSourceView view) { ... }
}

開發新版資料綁定控件:繼承 DataBoundControl

最小實作範例:SimpleList 控件


[ToolboxData("<{0}:SimpleList runat=server>")]
public class SimpleList : DataBoundControl
{
    // 1. 定義項目樣式
    [PersistenceMode(PersistenceMode.InnerProperty)]
    public Style ItemStyle { get; } = new Style();
    
    [PersistenceMode(PersistenceMode.InnerProperty)]
    public Style AlternatingItemStyle { get; } = new Style();
    
    // 2. 核心繫結邏輯
    protected override void PerformDataBinding(IEnumerable data)
    {
        base.PerformDataBinding(data);
        
        Controls.Clear();
        if (data == null) return;
        
        int index = 0;
        foreach (object item in data)
        {
            // 建立項目容器
            Control itemContainer = new Control();
            itemContainer.ID = $"item{index}";
            
            // 套用樣式
            Style style = (index % 2 == 0) ? ItemStyle : AlternatingItemStyle;
            style.ApplyTo(itemContainer);
            
            // 繫結資料 (簡化:直接 ToString)
            Literal lit = new Literal { Text = item?.ToString() ?? "" };
            itemContainer.Controls.Add(lit);
            
            Controls.Add(itemContainer);
            index++;
        }
        
        ChildControlsCreated = true;
    }
    
    // 3. 渲染輸出
    protected override void RenderContents(HtmlTextWriter writer)
    {
        writer.RenderBeginTag(HtmlTextWriterTag.Ul);
        foreach (Control item in Controls)
        {
            writer.RenderBeginTag(HtmlTextWriterTag.Li);
            item.RenderControl(writer);
            writer.RenderEndTag(); // li
        }
        writer.RenderEndTag(); // ul
    }
}

模板化資料綁定控件:繼承 BaseDataBoundControl

需支援 ItemTemplate、AlternatingItemTemplate 等模板時,繼承 BaseDataBoundControl 並實作 INamingContainer:


public class TemplatedList : BaseDataBoundControl, INamingContainer
{
    // 模板屬性
    [TemplateContainer(typeof(TemplatedListItem))]
    [PersistenceMode(PersistenceMode.InnerProperty)]
    public virtual ITemplate ItemTemplate { get; set; }
    
    [TemplateContainer(typeof(TemplatedListItem))]
    [PersistenceMode(PersistenceMode.InnerProperty)]
    public virtual ITemplate AlternatingItemTemplate { get; set; }
    
    // 項目容器類別
    public class TemplatedListItem : Control, INamingContainer, IDataItemContainer
    {
        public int ItemIndex { get; set; }
        public object DataItem { get; set; }
        public int DataItemIndex => ItemIndex;
        public int DisplayIndex => ItemIndex;
    }
    
    protected override void PerformDataBinding(IEnumerable data)
    {
        base.PerformDataBinding(data);
        Controls.Clear();
        
        if (data == null) return;
        
        int index = 0;
        foreach (object item in data)
        {
            TemplatedListItem container = new TemplatedListItem 
            { 
                ItemIndex = index, 
                DataItem = item 
            };
            
            // 實例化對應模板
            ITemplate template = (index % 2 == 0) ? ItemTemplate : AlternatingItemTemplate;
            template?.InstantiateIn(container);
            
            // 觸發 DataBinding 事件
            container.DataBind();
            
            Controls.Add(container);
            index++;
        }
        ChildControlsCreated = true;
    }
}

新舊架構對比與遷移建議

維度舊架構 (BaseDataList/DataGrid)新架構 (BaseDataBoundControl/DataBoundControl)
基類BaseDataList (針對列表)BaseDataBoundControl (通用基礎)
資料來源DataSource + DataBind()DataSourceID (宣告式) + 自動繫結
分頁/排序自行實作框架內建支援 (透過 DataSourceView)
模板支援固定模板集合彈性模板 + IDataItemContainer
樣式管理分散屬性Style 物件集中管理
設計時支援有限完整智慧標籤、樣式編輯器

最佳實踐建議

  1. 優先繼承 DataBoundControl:獲得內建分頁、排序、樣式、模板支援
  2. 實作 IDataItemContainer:模板容器支援 DataItem、DataItemIndex,啟用 Eval/Bind 語法
  3. 正確處理視圖狀態:樣式、模板、參數集合皆需實作 IStateManager
  4. 支援設計時體驗:加入 Designer、ToolboxBitmap、智慧標籤動作清單
  5. 遵循命名慣例:事件命名 ItemCreated、ItemDataBound、ItemCommand 等

總結

ASP.NET 2.0 重新設計資料綁定架構,核心在於關注點分離:資料綁定控件專注 UI 渲染與互動,資料源控件專注資料存取與操作。BaseDataBoundControl/DataBoundControl 提供豐富基礎設施,讓開發者專注業務邏輯而非基礎管線。掌握此架構,是開發專業級伺服器控件的關鍵門檻。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved