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&quot; />
                    <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