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 在浏览器中预览,效果如下图所示:

image 

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

源代码下载