Page.Eval方法可以幫助我們更好的撰寫數據綁定表達式,在ASP.NET 1.x時代,數據綁定表達式的一般形式是:
<%# DataBinder.Eval( Container , “DataItem.Name”) %>
而在ASP.NET 2.0中,同樣的代碼,我們可以這樣寫:
<%# Eval( “Name” )%>
ASP.NET 2.0是怎麼實現的呢?我們先從Eval方法來研究,通過反射.NET work 2.0類庫的源代碼,我們可以看到這個方法是這樣實現的:
protected internal object Eval(string expression)
{
this.CheckPageExists();
return DataBinder.Eval(this.Page.GetDataItem(), expression);
}
第一行我們不必管,這是檢查調用的時候有沒有Page對象的,如果沒有則會拋出一個異常。
關鍵是第二行:
return DataBinder.Eval(this.Page.GetDataItem(), expression);
Page.GetDataItem()也是2.0中新增的一個方法,用途是正是取代ASP.NET 1.x中的Container.DataItem。
看來不摸清楚GetDataItem()方法,我們也很難明白Eval的原理。GetDataItem的實現也很簡單:
public object GetDataItem()
{
if ((this._dataBindingContext == null) || (this._dataBindingContext.Count == 0))
{
throw new InvalidOperationException(SR.GetString("Page_MissingDataBindingContext"));
}
return this._dataBindingContext.Peek();
}
我們注意到了有一個內部對象_dataBindingContext,通過查源代碼發現這是一個Stack類型的東西。所以他有Peek方法。而這一段代碼很容易看懂,先判斷這個Stack是否被實例化,然後,判斷這個Stack裏面是不是有任何元素,如果Stack沒有被實例化或者沒有元素則拋出一個異常。最後是將這個堆棧頂部的元素返回。
ASP.NET 2.0用了一個Stack來保存所謂的DataItem,我們很快就查到了爲這個堆棧壓元素和彈出元素的方法:Control.DataBind方法:
protected virtual void DataBind(bool raiseOnDataBinding)
{
bool flag1 = false;//這個標誌的用處在上下文中很容易推出來,如果有DataItem壓棧,則在後面出棧。
if (this.IsBindingContainer)//判斷控件是不是數據綁定容器,實際上就是判斷控件類是不是實現了INamingContainer
{
bool flag2;
object obj1 = DataBinder.GetDataItem(this, out flag2);//這個方法是判斷控件是不是有DataItem屬性,並把它取出來。
if (flag2 && (this.Page != null))//如果控件有DataItem
{
this.Page.PushDataBindingContext(obj1);//把DataItem壓棧,PushDataBindingContext就是調用_dataBindingContext的Push方法
flag1 = true;
}
}
try
{
if (raiseOnDataBinding)//這裏是判斷是不是觸發DataBinding事件的。
{
this.OnDataBinding(EventArgs.Empty);
}
this.DataBindChildren();//對子控件進行數據綁定,如果這個控件有DataItem,則上面會將DataItem壓入棧頂,這樣,在子控件裏面調用Eval或者GetDataItem方法,就會把剛剛壓進去的DataItem給取出來。
}
finally
{
if (flag1)//如果剛纔有壓棧,則現在彈出來。
{
this.Page.PopDataBindingContext();//PopDataBindingContext就是調用_dataBindingContext的Pop方法
}
}
}
至此,我們已經可以完全瞭解ASP.NET 2.0中GetDataIten和Eval方法運作的原理了,下一次我打算研究ASP.NET 2.0中的新的Bind語法。
有提供Bind語法資料的和提出好建議的酌情給分,up、頂等分會很少,接分者無分。
關於效率:
我們可以比較一下各種方法:
((Type) Container.DataItem).Property
這種方法效率是最高的,因爲不存在任何反射。
其次是:
((Type) GetDataItem()).Property
這種方法效率差的原因在於多了一個Stack的Peek操作,當然,實際上這點兒差別可以忽略。
最後是:
Eval或者DataBinder.Eval,這兩種方法都使用反射來查找屬性或者索引器成員,效率大打折扣。
另外一個值得注意的問題是,所有實現了INamingContainer接口的Control,都應該實現IDataItemContainer接口,因爲在Control.DataBind的時候,如果發現控件實現了INamingContainer接口,就會試圖去尋找它的DataItem,如果這個控件沒有實現IDataItemContainer,則DataBinder.GetDataItem方法會使用反射看看控件有沒有一個叫做DataItem的屬性成員,顯然這不是我們希望看到的。
其實ASP.NET還有一個標記接口:INonBindingContainer,實現了INamingContainer接口的控件可以選擇同時實現這個來命令ASP.NET不去尋找DataItem,可是很可惜,不知道微軟出於什麼目的,這個接口是internal的……
其實效率方面不必太重視了,Eval表達式很好看的,即使有那麼極端的重視效率,GeDataItem也是不錯的選擇。毋庸置疑的是強類型轉換Container的效率是最高的,Eval最終是調用DataBinder.Eval方法,DataBinder.Eval是採用反射來獲取數據的,這顯然不如強類型數據轉換。
我們可以比較一下各種方法:
((Type) Container.DataItem).Property
這種方法效率是最高的,因爲不存在任何反射。
其次是:
((Type) GetDataItem()).Property
這種方法效率差的原因在於多了一個Stack的Peek操作,當然,實際上這點兒差別可以忽略。
最後是:
Eval或者DataBinder.Eval,這兩種方法都使用反射來查找屬性或者索引器成員,效率大打折扣。
另外一個值得注意的問題是,所有實現了INamingContainer接口的Control,都應該實現IDataItemContainer接口,因爲在Control.DataBind的時候,如果發現控件實現了INamingContainer接口,就會試圖去尋找它的DataItem,如果這個控件沒有實現IDataItemContainer,則DataBinder.GetDataItem方法會使用反射看看控件有沒有一個叫做DataItem的屬性成員,顯然這不是我們希望看到的。
其實ASP.NET還有一個標記接口:INonBindingContainer,實現了INamingContainer接口的控件可以選擇同時實現這個來命令ASP.NET不去尋找DataItem,可是很可惜,不知道微軟出於什麼目的,這個接口是internal的……
其實效率方面不必太重視了,Eval表達式很好看的,即使有那麼極端的重視效率,GeDataItem也是不錯的選擇。
*************************
而 <%# Eval( “Name” )%> 比 <%# DataBinder.Eval( Container , “DataItem.Name”) %> 還要多一步(多一層函數)。效率可想而知了。
當然也可能 2.0 會做什麼優化也說不定呢。
*********************
雙向數據綁定
與 DetailsView 控件一樣,FormView 通過其關聯的數據源控件支持自動 Update、Insert 和 Delete 操作。若要定義編輯或插入操作的輸入 UI,可在定義 ItemTemplate 的同時定義 EditItemTemplate 或 InsertItemTemplate。在本模板中,您將對輸入控件(如 TextBox、CheckBox 或 DropDownList)進行數據綁定,以綁定到數據源的字段。但是,這些模板中的數據綁定使用“雙向”數據綁定語法,從而允許 FormView 從模板中提取輸入控件的值,以便傳遞到數據源。這些數據綁定使用新的 Bind(fieldname) 語法而不是 Eval。
重要事項: 使用 Bind 語法進行數據綁定的控件必須設置有 ID 屬性。
<asp:FormView DataSourceID="ObjectDataSource1" DataKeyNames="PhotoID" runat="server">
<EditItemTemplate>
<asp:TextBox ID="CaptionTextBox" Text='<%# Bind("Caption") %>' runat="server"/>
<asp:Button Text="Update" CommandName="Update" runat="server"/>
<asp:Button Text="Cancel" CommandName="Cancel" runat="server"/>
</EditItemTemplate>
<ItemTemplate>
<asp:Label Text='<%# Eval("Caption") %>' runat="server" />
<asp:Button Text="Edit" CommandName="Edit" runat="server"/>
</ItemTemplate>
</asp:FormView>
在對 GridView 或 DetailsView 執行更新或插入操作時,如果該控件的列或字段定義了 BoundField,GridView 或 DetailsView 負責創建 Edit 或 Insert 模式中的輸入 UI,以便它能自動提取這些輸入值以傳遞迴數據源。由於模板包含任意的用戶定義的 UI 控件,因此,需要使用雙向數據綁定語法,這樣 FormView 等模板化控件才能知道應從模板中提取哪些控件值以用於更新、插入或刪除操作。在 EditItemTemplate 中仍然可以使用 Eval 語法進行不傳遞迴數據源的數據綁定。另請注意,FormView 與 DetailsView 和 GridView 一樣,支持使用 DataKeyNames 屬性保留主鍵字段(即使這些字段並未呈現)的原始值以傳遞迴更新/插入操作。
FormView 支持使用 DefaultMode 屬性指定要顯示的默認模板,但在默認情況下,FormView 以 ReadOnly 模式啓動並呈現 ItemTemplate。若要啓用用於從 ReadOnly 模式轉換爲 Edit 或 Insert 模式的 UI,可以向模板添加一個 Button 控件,並將其 CommandName 屬性設置爲 Edit 或 New。可以在 EditItemTemplate 內添加 CommandName 設置爲 Update 或 Cancel 的按鈕,以用於提交或中止更新操作。類似地,也可以添加 CommandName 設置爲 Insert 或 Cancel 的按鈕,以用於提交或中止插入操作。
下面的示例演示一個定義了 ItemTemplate 和 EditItemTemplate 的 FormView。ItemTemplate 包含使用 Eval(單向)綁定的控件,而 EditItemTemplate 包含一個使用 Bind 語句雙向綁定的 TextBox 控件。主鍵字段 (PhotoID) 是使用 DataKeyNames 屬性在視圖狀態中進行往返的。FormView 包含用於在其模板之間進行切換的命令按鈕。
C# Two-Way Databinding in a FormView Edit Template
通過使用添加到 Columns 或 Fields 集合的 TemplateField,GridView 和 DetailsView 還支持模板化 UI。TemplateField 支持使用 ItemTemplate、EditItemTemplate 和 InsertItemTemplate(僅 DetailsView)指定這些控件的不同呈現模式中的字段 UI。與上面的 FormView 示例一樣,EditItemTemplate 或 InsertItemTemplate 中的雙向綁定允許 GridView 或 DetailsView 從這些模板中的控件提取值。TemplateField 的常見用途是向 EditItemTemplate 添加驗證程序控件,用於 GridView 或 DetailsView 操作的聲明性驗證。下面的示例演示這種方法的一個示例。有關 ASP.NET 中可用的驗證控件的更多信息,請參考本教程的“驗證窗體輸入控件”部分。
C# Validation in a GridView Edit Template
TemplateField 的另一個用途是自定義輸入控件,這種控件用於輸入 GridView 或 DetailsView 列/字段值。例如,可以將 DropDownList 控件放在 TemplateField 的 EditItemTemplate 中,以允許從預定義的值列表進行選擇。下面的示例演示這種方法。注意,本例中的 DropDownList 與它自己的數據源控件進行了數據綁定,以便動態地獲取該列表的值。
C# DropDownList in a GridView Edit Template