【茶包射手專欄】Why Bind("Blah") Doesn't Work?
0 |
寫TemplatedControl時,ASP.NET 2.0有個好用的新函數Eval("FieldName"),可以在DataGrid、GridView等物件的Template Column中,將DataItem中某個欄位的值指定給Label、TextBox之類的;在ASP.NET 1.1時代,這得寫成DataBinder.Eval(Container, "DataItem.FieldName"),相較之下,新寫法簡潔多了! 我查到大陸朋友寫的一篇精彩文章(同一篇文章被轉貼得到處都是[Goggle共找到1350處 orz],但幾乎都沒註明原出處,只好隨便貼一個URL給大家參考,並向不知名的原作者致上敬意),用Reflection的技巧去追出其實Eval("FieldName")內部呼叫了DataBinder.Eval(this.Page.GetDataItem(), "FieldName"),文中甚至還探討了不同做法的效能,這下Eval函數的原理就很清楚了。
Eval有個好兄弟叫Bind,用在雙向的Binding上,例如EditItemTemplate中,需將User在TextBox輸入的內容回寫到DataItem。
同事小娟問了一個有趣的問題,她發現在DataBinding中呼叫自訂函數時,<%# MyFunc(Eval("FieldName") %>的寫法可以過關,但<%# MyFunc(Bind("FieldName") %>卻會傳回
Compiler Error Message: CS0103: The name 'Bind' does not exist in the current context
我認為這是因為Data-Binding Syntax裡,並不是直接將整段內容當成Source Code拿來Compile,而是進行了一些Parsing及轉換。但推論歸推論,要怎麼證明呢?
1: <form id="form1" runat="server">
2: <div>
3: <asp:GridView ID="GridView1" runat="server"
4: AutoGenerateColumns="False">
5: <Columns>
6: <asp:TemplateField HeaderText="NameEval">
7: <ItemTemplate>
8: <asp:Label ID="Label1" runat="server"
9: Text='<%# Eval("Name_Eval") %>'></asp:Label>
10: </ItemTemplate>
11: </asp:TemplateField>
12: <asp:TemplateField HeaderText="NameBind">
13: <ItemTemplate>
14: <asp:Label ID="Label2" runat="server"
15: Text='<%# Bind("Name_Bind") %>'></asp:Label>
16: </ItemTemplate>
17: </asp:TemplateField>
18: <asp:TemplateField HeaderText="NameFuncEval">
19: <ItemTemplate>
20: <asp:Label ID="Label3" runat="server"
21: Text='<%# CvrtStr(Eval("Name_Func_Eval")) %>'></asp:Label>
22: </ItemTemplate>
23: </asp:TemplateField>
24: <asp:TemplateField HeaderText="NameFuncBind">
25: <ItemTemplate>
26: <asp:Label ID="Label4" runat="server"
Text='<%# CvrtStr(Bind("Name_Func_Bind")) %>'></asp:Label>
28: </ItemTemplate>
29: </asp:TemplateField>
30:
31: </Columns>
32: </asp:GridView>
33: </div>
34:
35: </form>
以上的程式碼是跑不動的,你會得到前述找不到Bind的錯誤,但錯誤頁面本身其實已提供了線索,只是常被忽略而已。我們都知道ASPX的Code最後也會被Parsing成.cs/.vb即時Compile再與Code-Beside/Code-Behind的DLL一起整合執行,當ASPX端程式編譯失敗時,下方會出現:
Show Detail Compiler Output:
Show Complete Compilation Source:
按下Show Complete Compilation Source,就可以看到ASPX怎麼變成一個複雜的.cs。Trace其中,可以找到前面程式中四個Binding分別被轉成:
1.Eval("Name_Eval")
dataBindingExpressionBuilderTarget.Text = System.Convert.ToString( Eval("Name_Eval") , System.Globalization.CultureInfo.CurrentCulture);
2.Bind("Name_Bind")
dataBindingExpressionBuilderTarget.Text = System.Convert.ToString(this.Eval("Name_Bind"), System.Globalization.CultureInfo.CurrentCulture);
3.CvrtStr(Eval("Name_Func_Eval"))
dataBindingExpressionBuilderTarget.Text = System.Convert.ToString( CvrtStr(Eval("Name_Func_Eval")) , System.Globalization.CultureInfo.CurrentCulture);
4.CvrtStr(Bind("Name_Func_Bind")
dataBindingExpressionBuilderTarget.Text = System.Convert.ToString( CvrtStr(Bind("Name_Func_Bind")) , System.Globalization.CultureInfo.CurrentCulture);
答案揭曉了,原來根本不存在Bind這個Method,當我們寫Bind時,實際上它會被轉成this.Eval("FieldName"),而當它被包成自訂函數CvrtStr的參數時,則不會被解讀,而保持Bind(..)的寫法,導致Bind函數不存在的Build Error!
第一個迷團解開了,但如果Bind與Eval的差別只在於有沒有加this.並不合情理,如文件所說,Bind主要用來雙向的Data-Binding,所以應該會與Eval有明顯不同才對。仔細再挖下去,在無法Build的ASPX Source Code中,我們可以找到另一段Code:
public System.Collections.Specialized.IOrderedDictionary
@__ExtractValues__control8(System.Web.UI.Control @__container)
{
System.Collections.Specialized.OrderedDictionary @__table;
System.Web.UI.WebControls.TextBox TextBox1;
#line 13 "C:\WWW\TestWeb\BindTest.aspx"
TextBox1 = ((System.Web.UI.WebControls.TextBox)
(@__container.FindControl("TextBox1")));
#line default
#line hidden
#line 13 "C:\WWW\TestWeb\BindTest.aspx"
@__table =
new System.Collections.Specialized.OrderedDictionary();
#line default
#line hidden
#line 13 "C:\WWW\TestWeb\BindTest.aspx"
if ((TextBox1 != null)) {
@__table["Name_Bind"] = TextBox1.Text;
}
#line default
#line hidden
return @__table;
}
Bingo!! 這就是Bind與Eval的最大差別!!
當ASP.NET在Parsing Data-Binding Syntax時,遇到了Bind,除了加上設定TextBox.Text的程式碼,還會加上將TextBox.Text存入OrderedDictionary的邏輯,換句話說,之前的推論沒有錯: Bind算是Syntax,而非特定的Method。
真相大白,收工!
Comments
Be the first to post a comment