1. 前言
開發項目中經常會遇到要求使用者挑選清單中資料的問題,例如選擇允許參加某個活動的使用者、選擇允許參加某個項目的省市或地區,在資料量較小的情況下,我們可以把所有資料使用任何一種資料繫結控制項顯示出來並在每一個資料項目前面顯示複選框,但在資料量比較大時同時顯示全部資料顯然不太合適,我們首先考慮到的是使用分頁。但是在使用分頁後問題也隨之而來,如何在切換分頁時同時保持已選中項目,有些開發人員可能使用伺服器端代碼編寫儲存在Session中,這種方式最大的弊端在於每次都向伺服器端提交,並且由於這些選中的資料只需要在當前頁面使用,所以還白白的佔用了Session。
對於這類問題,我們現在有了更好的選擇,使用ASP.NET AJAX和JQuery結合在一起實現翻頁選擇。
2. 相關理論
使用ASP.NET AJAX中的ScriptManager和UpdatePanel控制項我們可以輕鬆的實現GridView的分頁,現在所需要考慮的就是在分頁結束後根據使用者的選擇使相應的複選框處於選中狀態。實際上在AJAX.NET中,微軟為我們提供了完全的AJAX請求生命週期控制,以下事件會在用戶端觸發:
- Application.init——當某一個頁面第一次請求時觸發。在非同步回傳中不會觸發該事件
- PageRequestmanager.initializeRequest——在一個非同步請求開始之前觸發,相當於預始化階段
- PageReqeustManager.beginRequest——在一個非同步請求開始之前觸發
- PageRequestManager.pageLoading——在用戶端收到服務端的非同步請求響應並且更新UpdatePanel之前觸發該事件
- PageRequestManager.pageLoaded——在用戶端收到服務端的非同步請求響應並且更新UpdatePanel中的內容更新後觸發該事件,頁面初始化載入時也會觸發該事件。
- Application.load——在普通和非同步回傳期間觸發該事件
- PageRequestManager.endRequest——在完成一次非同步請求後不管有沒有異常發生均會觸發該事件
- Application.unload——當使用者離開或重新載入該頁面時觸發該事件
通過以上分析,我們只需要在分頁非同步請求完成後根據選中的狀態更新相應的複選框即可。在取得複選框時可以使用JQuery中強大的選取器,例如以下代碼選中了某個GridView中的全部複選框。
$('div[#<%=gdvCustom.ClientID %>] input[type=checkbox]')
根據以上分析,以下範例程式碼展示了最終的運用結果。
3. 解決方案樣本
本例中使用了Northwind資料庫,展示了選擇客戶(Customers表)的效果。
3.1 建立解決方案並建立ASP.NET網站。
3.2 在網站下建立js目錄並將jquery指令檔複製到該目錄下。
3.3 使用LINQ to SQL訪問資料庫。首先在網站中建立App_Code目錄,在該目錄下建立Northind.dbml檔案,使用伺服器總管建立到資料庫的串連並將Customers表拖放到dbml檔案的設計器上。
3.4 開啟Default.aspx頁面並切換到設計檢視,拖放ScriptManager和UpdatePanel控制項並重新命名為smCustomer(如此邪惡的縮寫)和upCustomer。
3.5 向UpdatePanel中放置GridView控制項並重新命名為gdvCustom,使用其快捷標記建立使用NorthinwdDataContext的LINQ資料來源並指定要顯示的表為Customers,最後根據需要刪除不必要的列,但至少需要儲存CustomerID和ContactName列。
3.6 向GridView控制項中添加模版列顯示複選框,並設定其value和title屬性分別為客戶的編號和連絡人姓名,同時為了更好的協助使用者瞭解當前選中的客戶,向頁面中加入一個列表。完成後表單中的html代碼如下所示:
<div> <asp:ScriptManager ID="smCustom" runat="server"> </asp:ScriptManager> <asp:UpdatePanel ID="upCustom" runat="server"> <ContentTemplate> <asp:GridView ID="gdvCustom" runat="server" AllowPaging="True" AutoGenerateColumns="False" DataSourceID="ldsNorthwind" Width="100%"> <Columns> <asp:TemplateField HeaderText="選擇"> <ItemTemplate> <input type="checkbox" value='<%#Eval("CustomerID") %>' title='<%#Eval("ContactName") %>' onclick='changeSelect(this);' /> </ItemTemplate> </asp:TemplateField> <asp:BoundField DataField="CustomerID" HeaderText="CustomerID" ReadOnly="True" SortExpression="CustomerID" /> <asp:BoundField DataField="CompanyName" HeaderText="CompanyName" ReadOnly="True" SortExpression="CompanyName" /> <asp:BoundField DataField="ContactName" HeaderText="ContactName" ReadOnly="True" SortExpression="ContactName" /> <asp:BoundField DataField="ContactTitle" HeaderText="ContactTitle" ReadOnly="True" SortExpression="ContactTitle" /> <asp:BoundField DataField="Address" HeaderText="Address" ReadOnly="True" SortExpression="Address" /> </Columns> </asp:GridView> <asp:LinqDataSource ID="ldsNorthwind" runat="server" ContextTypeName="NorthwindDataContext" Select="new (CustomerID, CompanyName, ContactName, ContactTitle, Address)" TableName="Customers"> </asp:LinqDataSource> </ContentTemplate> </asp:UpdatePanel> <h3> 已選擇客戶</h3> <select size="5" id="selCustom"> </select> <input type="button" id="btnSave" value="儲存" /></div>
3.7 首先加入JQuery指令碼引用:
<script type="text/javascript" src="js/jquery-1.3.2.js"></script>
3.8 添加指令碼在AJAX請求完成後根據使用者選擇更新複選框顯示(需要注意該端指令碼需放置到ScritpManager下方否則會提示某些對象未定義):
<script type="text/javascript"> Sys.Application.add_init(application_init); function application_init() { var prm = Sys.WebForms.PageRequestManager.getInstance(); prm.add_endRequest(prm_endRequest); } function prm_endRequest() { $('div[#<%=gdvCustom.ClientID %>] input[type=checkbox]').each(function() { var lst = $('#selCustom')[0]; var contains = false; var imgIndex = -1; for (var i = 0; i < lst.options.length; i++) { if (lst.options[i].value == this.value) { contains = true; break; } } if (contains) { this.checked = true; } }); }</script>
3.9 定義指令碼在頁面載入完成後為相應的列表和按鈕控制項處理雙擊和單擊事件,以實現項目的移除和選擇項目的提示:
$(function() { $('#selCustom').dblclick(function() { var lst = this; $('div[#<%=gdvCustom.ClientID %>] input[type=checkbox]').each(function() { if (this.value == lst.options[lst.selectedIndex].value) this.checked = false; }); this.options.remove(this.selectedIndex); }); $('#btnSave').click(function() { var customers = '['; var count = 0; var lst = $('#selCustom')[0]; for (var i = 0; i < lst.options.length; i++) { customers += lst.options[i].value; customers += ','; count++; } if (count > 0) { customers = customers.substr(0, customers.length - 1); } customers += ']'; alert(customers); });});
3.10 編寫changeSelect函數實現在點擊複選框時向列表框中添加或移除相應的客戶:
function changeSelect(chk) { var lst = $('#selCustom')[0]; var contains = false; var cusIndex = -1; for (var i = 0; i < lst.options.length; i++) { if (lst.options[i].value == chk.value) { contains = true; cusIndex = i; break; } } if (contains) { lst.options.remove(cusIndex); } else { var opt = document.createElement("option"); opt.text = chk.title; opt.value = chk.value; lst.options.add(opt); }};
3.11 在瀏覽器中預覽,效果如所示:
4. 總結
ASP.NET AJAX為開發人員提供了方便的實現非同步請求的方法,通過ScriptManager和UpdatePanel,幾乎不需要編寫任何代碼就可以實現非同步請求,同時JQuery提供的靈活的選取器可以協助我們更好的控制頁面元素,將這兩者結合起來發揮各自的優勢,可以在為開發人員節省很多時間的同時編寫出強大的ASP.NET應用。
5. 問題
5.1 本例中只實現了相應資料的選擇並儲存到用戶端JavaScript數組中,還差最後一公裡才能實現完整的功能,您可以嘗試使用JQuery完成AJAX請求,最終實現選擇客戶的功能。
5.2 另外,本例中還有一個陷井,在為表格中的複選框設定事件時沒有使用JQuery而是直接使用onclick事件完成,這破壞了一些JQuery的初衷,如果這裡同樣使用JQuery在頁面載入完成後使用選取器為複選框附加事件,是否可以?
5.3 本例中的JavaScript中有些複製、粘貼的方法,是否可以將其封裝成公用的函數呢?
6. 參考
- Stephen Walther.ASP.NET3.5 Unleashed:Sams,2007
- Bear Bibeault,Yehuda Katz.JQuery in Action.陳寧:人民郵電出版社,2009
原始碼下載