在上一篇《深入ASPNET數據綁定(上)》中我們分析了在NET中的數據綁定語法的一些內部機理簡單說來就是ASPNET在運行時為我們完成了頁面的動態編譯並解析頁面的各種服務器端代碼包括數據綁定語法而數據綁定的語法雖是一些<%# %>代碼塊在生成的代碼中仍然使用了服務器端控件以及在DataBinding事件調用DataBinderEval方法來完成數據的綁定工作所有的數據綁定模板控件都使用了這樣的機制來進行數據的單向綁定在NET 中新增了雙向的數據綁定方式主要用在GridViewDetailsViewFormView等數據容器控件中結合DataSourceControl就可以非常輕松的完成數據的更新和提交工作而不需要我們手工去遍歷輸入控件的值那在這樣的雙向數據綁定中ASPNET又是做了哪些工作來為我們透明輸入控件與字段的取值與對應關系讓我們可以在DataSouceControl中方便得到數據項修改前的值和修改後的值?下面就讓我們一起來從一段頁面代碼開始吧
: <asp:DetailsDataSouce ID=DetailsDataSouce runat=server>
: </asp:DetailsDataSouce>
: <asp:DetailsView ID=detailsView runat=server DefaultMode=Edit DataSourceID=DetailsDataSouce>
: <Fields>
: <asp:TemplateField>
: <HeaderTemplate>
: 電流:</HeaderTemplate>
: <EditItemTemplate>
: <asp:TextBox ID=textBox runat=server Text=<%# Bind([電流{a}]) %>></asp:TextBox>
: </EditItemTemplate>
: </asp:TemplateField>
: </Fields>
: </asp:DetailsView>
在一個頁面中定義了如上的一個DetailsView控件為這個控件指定了ID為DetailsDataSource的DataSouceControl控件這個控件是我們自己定義的一個DataSourceControl它返回的數據字段包括ID電流{a}電壓(v)備注名稱]我並沒有設置DetailsView的AutoGenerateRows屬性的值默認情況下它是為我們自動的生成這些字段的對應的數據顯示和輸入控件除此之外我們還另外添加了一個數據模板字段在這個模板中指定了編輯模板在編輯模板中我使用了<%# Bind()%>這樣的語法將textBox與[電流{a}]字段雙向綁定起來
為什麼這裡的字段都有一些特殊呢?因為我原先的意圖是除了分析綁定語法以外還要測試哪些特殊字符無法使用數據綁定語法來綁定數據的這個在下篇文章中會具體介紹
Bind與Eval不一樣這樣的Bind並不Page或TemplateControl的一個方法事實上我們應該把它當成一個關鍵字來看待因為在ASPNET的雙向數據綁定當中並沒有這樣的一個函數存在它的存在是只是告訴ASPNET動態編譯頁面類時將這個語法編譯成一定的代碼格式並生成一些函數代理來達到雙向數據交流的目的
那麼這一段代碼動態編譯生成的服務器代碼又是如何的呢?讓我們反編譯動態程序集裡面會找到用於創建DetailsView的__BuildControldetailsView的私有方法在這裡會調用到一些其它內部方法我們不要讓這些方法來干擾我們的視線直接找到創建如上模板字段的方法
: [DebuggerNonUserCode]
: private TemplateField __BuildControl__control()
: {
: TemplateField field = new TemplateField();
: fieldHeaderTemplate = new CompiledTemplateBuilder(new BuildTemplateMethod(this__BuildControl__control));
: fieldEditItemTemplate = new CompiledBindableTemplateBuilder(new BuildTemplateMethod(this__BuildControl__control) new ExtractTemplateValuesMethod(this__ExtractValues__control));
: return field;
: }
這裡首先把this__BuildControl__control作為一個代理函數用於創建頭部模板的內容也就是如上的電流字段標題然後才是創建EditItemTemplate這個模板又被一些的中介模板所代替我們只需要來關心this__BuildControl__control和__ExtractValues__control即可__BuildControl__control是為了編輯數據字段時將數據字段的值顯示在輸入控件中(輸入控件的初始化即字段值綁定到輸入控件中)而__ExtractValues__control則是在提交數據時要找出這個模板內所有的雙向綁定字段將這些字段的值以綁定字段名為Key以輸入控件的值為Value添加了IOrderedDictionary字典中DetailsView等數據綁定控件調用這些委托代理來收集所有的被雙向綁定的字段的最新的值下面分別是兩段函數的代碼片段
: [DebuggerNonUserCode]
: private TextBox __BuildControl__control()
: {
: TextBox box = new TextBox();
: boxTemplateControl = this;
: boxApplyStyleSheetSkin(this);
: boxID = textBox;
: boxDataBinding += new EventHandler(this__DataBinding__control);
: return box;
: }
: public void __DataBinding__control(object sender EventArgs e)
: {
: TextBox box = (TextBox) sender;
: IDataItemContainer bindingContainer = (IDataItemContainer) boxBindingContainer;
: if (thisPageGetDataItem() != null)
: {
: boxText = ConvertToString(baseEval([電流{a}]) CultureInfoCurrentCulture);
: }
: }
: [DebuggerNonUserCode]
: public IOrderedDictionary __ExtractValues__control(Control __container)
: {
: TextBox box = (TextBox) __containerFindControl(textBox);
: OrderedDictionary dictionary = new OrderedDictionary();
: if (box != null)
: {
: dictionary[[電流{a}]] = boxText;
: }
: return dictionary;
: }
由上面的代碼片段可以了解到ASPNET動態編譯器是將Bind語法拆分為兩部分綁定輸出和讀取輸入控件值綁定輸出部分與前篇介紹的機制是完全一樣的並且也是調用DataBinderEval方法來綁定數據而讀取輸入控件值則是會根據頁面上控件的類型以及綁定的控件屬性名稱生成一段強類型的控件屬性讀取代碼並將控件的值保存到dictionay中返回出去而它全然不知容器控件是如何將這些值合並起來傳給對應的DataSouceControl控件的
關於數據容器控件而何與DataSouceControl協同工作並不是我們這裡要分析的重點但是我們可以簡單的描述一下工作流程以DetailsView的數據更新為例大家通過反編譯DetailsView的源碼會找到名稱為HandleUpdate的私有方法在這個方法裡面會去處理數據項更新前的值(至於在Web環境中如何保存更新前的值就需要靠ViewState的強大功能了)和更新後的值(通過ExtractRowValues函數調用類似上面生成的__ExtractValues__control代理函數來收集所有雙向綁定字段的值存到NewValues裡面)並將他們分別保存在兩個不同的IOrderedDictionary對象(OldValuesNewValues)中然後將調用對應的DataSouceView的Update方法傳入原字段值和新字段值和一些必須的參數即可由我們通過重寫DataSourceView的方法來得到所有需要更新字段的原始值和新值並可以對比比較哪些字段值是否發生了變化NBearDataSource控件就是利用了這樣的機制來直接重DataSourceControl和DataSourceView來達到數據的全自動修改和添加方案的
這裡還有一點不得不說在GridViewDetailsView並不一定需要使用<%# Bind()%>語法來實現數據的雙向綁定他們的字段雙向綁定可以通過BoundField及它的子控件代替模板控件的綁定語法一樣可以達到雙向綁定的目的簡單但沒有模板來得靈活而在存取不同版本的字段值也是類似的機制
由於這部分涉及到的都是動態和內部代碼如果沒有親自去閱讀這些代碼估計還是很難理解最後我們再來簡單總結一下ASPNET在模板中雙向綁定字段是通過<%# Bind() %>這樣的語法但是Bind我們更應該把它理解為是一個關鍵字而不是一個函數因為在ASPNET的控件中並沒有存在這個函數ASPNET運行時在編譯頁面代碼時會把Bind關鍵字的代碼當成兩部分來編譯一部分是單向綁定代碼另一部分而是讀取對應輸入控件的綁定屬性以綁定字段名為Key添加到IOrderedDictionary中收集返回給數據容器控件(GridViewDetailsViewFormView)等讓它們處理
總體來說ASPNET 的雙向綁定機制給我們在提交數據時帶來了極大的方便盡管有些人很排斥DataSourceControl的模式但是我們不可否認合理應用會大大提高我們的開發效率希望通過這兩篇的介紹我們能對ASPNET數據綁定機制有更多的認識在下一篇的文章中我們將會介紹一些關於數據綁定方式性能以及對字段名的局限性等相關主題
From:http://tw.wingwit.com/Article/program/net/201311/13082.html