利用 IDataItemContainer.DataItem 取資料項目的值

利用 IDataItemContainer.DataItem 取資料項目的值

資料繫結控制項 (Data-Bound Controls) 取值的方法之前寫過 (參考:),當時說過資料繫結可分為呈現 (Layout) 以及資料 (Data) 兩部分,正因呈現的內容與資料來源有密切關係,所以才稱為「資料繫結」控制項。這麼說來要取資料繫結控制項的欄位值,透過底層資料物件下手應該可行…此話怎講?原因是各資料繫結控制項對應底層資料列的方式或有不同,但必定實作了 IDataItemContainer 介面,查一下 MSDN Library 得知其中的 DataItem 屬性是可以利用的對象。

一般而言存取資料最常用的方式不外乎使用資料來源控制項,或是撰寫程式碼取得資料物件 (DataSet、DataTable 等),兩種作法回傳的都是表格式資料,細分下去便是 DataRowView 類別,代表一筆資料列 (DataRow) 的一個版本,我們參考 GridViewRow.DataItem 屬性 這篇的範例:
{
    if (e.Row.RowState == DataControlRowState.Edit)
    {
        // 取得底層資料物件(本例中是一 DataRowView 物件。)
        System.Data.DataRowView rowView = (System.Data.DataRowView)e.Row.DataItem;

        // 取得當前資料列的 state 值 
        String state = rowView["state"].ToString();

        // 抓取當前資料列的 StatesList 下拉選單 
        DropDownList list = (DropDownList)e.Row.FindControl("StatesList");

        // 定住  StatesList 下拉選單選取值
        ListItem item = list.Items.FindByText(state);
        list.SelectedIndex = list.Items.IndexOf(item);
    }
}

這一段的用意是取得底層資料物件中的 state 值,用它來定住編輯狀態下 DropDownList 呈現的預設值,效果等同在原始檔設定資料繫結:
                            runat="server" SelectedValue='<%# Eval("state") %>' />



本文的重點是另一種狀況,因為實務上自訂資料物件的寫法也很常見(只要實作 IListIDataSourceIListSource 介面其中之一的類別就能當資料來源)。底下新增兩個類別,DAL 類別有一個 GetStockData() 方法,負責回傳 ArrayList 物件,其包含了自訂型別 Stock:
public class Stock
{
    private int key;

    /// <summary>
    /// 主識別值
    /// </summary>
    public int Key
    {
        get { return key; }
        set { key = value; }
    }

    private string name;

    /// <summary>
    /// 個股名稱
    /// </summary>
    public string Name
    {
        get { return name; }
        set { name = value; }
    }

    private string ticker;

    /// <summary>
    /// 個股代號
    /// </summary>
    public string Ticker
    {
        get { return ticker; }
        set { ticker = value; }
    }

    /// <summary>
    /// 初始化個股類別的新執行個體
    /// </summary>
    /// <param name="name">個股名稱</param>
    /// <param name="ticker">個股代號</param>
    public Stock(int key, string name, string ticker)
    {
        this.key = key;
        this.name = name;
        this.ticker = ticker;
    }
}

// DAL.cs
using System.Collections;

public class DAL
{
    public DAL() { }

    public static ArrayList GetStockData()
    {
        ArrayList values = new ArrayList();

        values.Add(new Stock(1, "Microsoft", "Msft"));
        values.Add(new Stock(2, "Intel", "Intc"));
        values.Add(new Stock(3, "Dell", "Dell"));

        return values;
    }
}

現在新增一個網頁,利用 DAL.GetStockData() 取回資料給 GridView 呈現,接下來示範用 IDataItemContainer.DataItem 的方式取值,請看底下程式碼(為方便展示故採單一檔案程式碼模型):
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">

    private void BindData()
    {
        ArrayList samples = DAL.GetStockData();

        MyGridView.DataSource = samples;
        MyGridView.DataBind();
    }

    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
            BindData();
    }

    protected void MyGridView_RowEditing(object sender, GridViewEditEventArgs e)
    {
        MyGridView.EditIndex = e.NewEditIndex;
        BindData();
    }

    protected void MyGridView_RowCancelingEdit(object sender, GridViewCancelEditEventArgs e)
    {
        MyGridView.EditIndex = -1;
        BindData();
    }

    protected void MyGridView_RowUpdating(object sender, GridViewUpdateEventArgs e)
    {

    }

    protected void MyGridView_RowDataBound(object sender, GridViewRowEventArgs e)
    {
        GridViewRow currentRow = e.Row;

        if ((currentRow.RowState & DataControlRowState.Edit) > 0)
        {
            // 取得底層資料物件(本例是 Stock 類別。)
            Stock s = (Stock)currentRow.DataItem;

            ClientScript.RegisterStartupScript(
                GetType(),
                "_TEST",
                String.Format("alert('DataItem - Key: {0}, Name : {1}, Ticker : {2}.');", s.Key, s.Name, s.Ticker),
                true);
        }
    }
    
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>GridViewRow DataItem Example - Custom Data Object.</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <h3>
            GridViewRow DataItem Example</h3>
        <asp:GridView ID="MyGridView" AutoGenerateColumns="false" AutoGenerateEditButton="true"
            DataKeyNames="Key" runat="server" OnRowEditing="MyGridView_RowEditing" OnRowCancelingEdit="MyGridView_RowCancelingEdit"
            OnRowDataBound="MyGridView_RowDataBound" OnRowUpdating="MyGridView_RowUpdating">
            <Columns>
                <asp:BoundField DataField="Name" HeaderText="Name" />
                <asp:BoundField DataField="Ticker" HeaderText="Ticker" />
            </Columns>
        </asp:GridView>
    </div>
    </form>
</body>
</html>

取回資料項目的寫法跟前例類似,惟必須轉型為自訂型別 Stock 而不是 DataRowView,這樣就能存取所屬成員了,是不是很簡單呢?

這個寫法在你需要取得原始資料時可以考慮使用,因為有時候為了美觀或特殊用途會在呈現時格式化輸出或做一些轉換,若為了取得原始資料再回頭去撈資料庫似乎不是很划算。最後要提醒大家的是 DataItem 屬性只有在資料繫結(例如 GridView.RowDataBound、DetailsView.DataBound、ListView.ItemDataBound)發生的當下或以後才能使用,否則會回傳 null,這點在使用時要謹記囉!


## 各資料繫結控件所對應的 DataItem 屬性: