• [编]使用CascadingDropDown实现级联式下拉框


    使用CascadingDropDown实现级联式下拉框

    引言

    级联式下拉框,有时也叫联动下拉框,也是Web页面中一个很常见的功能。就是假设有若干个下拉框,比如说有3个,分别显示 省份、城市、街道,当选择某一省份后,城市下拉框仅显示属于该省的城市,选择好城市之后,街道下拉框仅显示属于该城市的街道。记得以前做Asp的时候,做一个类似这样的下拉框需要花费不少的力气,编写不少的javascript代码。如今到了Asp.Net时代,使用Ajax Control Toolkit中的CascadingDropDown控件,再配合普通的DropDownList控件以及Web Serive,实现这样一个功能变得非常容易了。本文将一步步来实现它。

    数据库和数据访问

    这一部分和我们本文要讨论的主题实际上没有关系,但是我们需要“可以级联显示”的数据,因此需要创建一个范例数据库,同时还需要有对它的数据访问方法。对于这部分,我仅给出简单的步骤,依然以省份、城市、街道为例:

    1. 新建一个解决方案,添加网站,并且在App_Data中创建一个数据库SiteDB。
    2. 在SiteDB中创建三张表,分别为Province(Id, Name),City(Id, Name, ProvinceId),Street(Id, Name, CityId)。其中Id为int自增类型,且为主键;Name为Varchar(50)。ProvinceId和CityId则为外键。
    3. 为上面三个表添加一些范例数据,如果你觉得麻烦,那么可以拷贝本文附带代码中的数据库。
    4. 在App_Code中添加一个SiteDataSet数据集。然后打开“Server Explore(服务器浏览器)”将上面三张表拖进去。
    5. 分别对它们的TableAdapter进行配置,其中Province的为获得全部数据,而City和Street分别根据ProvinceId和CityId获取数据,最终的SiteDataSet如下图所示:

    OK,这样数据库和数据访问就完毕了,接下来我们看下Web页面。

    Web页面布局和设置

    首先我们在站点中添加一个“Ajax-enabled WCF Service(启用了AJAX的WCF服务)”,命名为AddressService,暂时不用管它的实现。在ScriptManager中对它进行注册(在本文中这里不注册也可以):

    <asp:ScriptManager ID="ScriptManager1" runat="server">
        <Services>
            <asp:ServiceReference Path="~/AddressService.svc" />
        </Services>
    </asp:ScriptManager>

    打开Default.aspx,像下图这样对页面进行布局:

    三个DropDownList分别的Id为ddlProvince、ddlCity、ddlStreet。之后从AjaxControlToolkit中拖三个CascadingDropDown到页面上,像下面这样对其进行配置:

    <ajaxControlToolkit:CascadingDropDown ID="CascadingDropDown1"
            runat="server"
            TargetControlID="ddlProvince"
            Category="Province"
            PromptText="请选择省份...."
            LoadingText="加载中,请稍后 ..."
            ServicePath="AddressService.svc"
            ServiceMethod="GetProvince"
            >
    </ajaxControlToolkit:CascadingDropDown>
    <ajaxControlToolkit:CascadingDropDown ID="CascadingDropDown2"
            runat="server"                 
            TargetControlID="ddlCity"
            ParentControlID="ddlProvince"
            Category="City"
            PromptText="请选择城市...."
            LoadingText="加载中,请稍后 ..."
            ServicePath="AddressService.svc"
            ServiceMethod="GetCity"
            >
    </ajaxControlToolkit:CascadingDropDown>    
    <ajaxControlToolkit:CascadingDropDown ID="CascadingDropDown3"
            runat="server"                 
            TargetControlID="ddlStreet"
            ParentControlID="ddlCity"
            Category="Street"
            PromptText="请选择城市...."
            LoadingText="加载中,请稍后 ..."
            ServicePath="AddressService.svc"
            ServiceMethod="GetStreet"
            >
    </ajaxControlToolkit:CascadingDropDown>

    这三个CascadingDropDown分别对应之前添加的通常的DropDownList控件,下面是对其各个属性的一个简单说明:

    • TargetControlID:与之协作的DropDownList控件的ID。
    • ParentControlID:父级下拉框的ID,显然顶级下拉框ddlProvince不含有父级下拉框,因此不含有ParentControlID。
    • Category:此下拉框的“类别”,也可以称为名称,具体使用到后面再说,这里说了也不好理解。
    • PromptText:进行选择之前显示的文本。
    • LoadingText:加载时显示的文本,因为需要与服务端进行通信,所以难免会产生延迟,这里的文本便是在延迟时显示的文本。
    • ServicePath:获取数据时的Web服务。此处即为我们的WCF服务。
    • ServiceMethod:获取数据的方法。这些方法由WCF服务实现,显然我们暂时并没有实现它。

    好了,现在页面部分就设置完毕了,我们最后看下WCF服务的实现,它仅仅是使用第一步创建的数据集来获取数据。

    编写WCF服务代码

    WCF服务所要实现的实际就是上面CascadingDropDown中所设置的三个用于获取数据的方法:GetProvince()、GetCity()和GetStreet()。因为需要在页面的后置代码中使用AjaxControlToolkit所定义的类型,所以如果你和我一样使用了GAC来部署AjaxControlToolkit程序集的话,那么需要首先在Web.Config中对它进行注册:

    <compilation debug="true">
        <assemblies>
            <add assembly="AjaxControlToolkit, Version=3.0.20820.37372, Culture=neutral, PublicKeyToken=28f01b0e84b6d53e"/>
            <!-- 省略其他 -->
        </assemblies>
    </compilation>

    接下来我们来实现这三个方法,先看App_Code中AddressService下的GetProvince()方法的实现,显然,它获得所有的省份:

    public class AddressService
    {
        [OperationContract]
        public CascadingDropDownNameValue[] GetProvince(string knownCategoryValues, string category)
        {
            Thread.Sleep(1000); // 等待1秒,模拟网络延迟

            SiteDataSetTableAdapters.ProvinceTableAdapter provinceAdapter
                = new SiteDataSetTableAdapters.ProvinceTableAdapter();

            SiteDataSet.ProvinceDataTable provinceTable = provinceAdapter.GetProvince();
            List<CascadingDropDownNameValue> values = new List<CascadingDropDownNameValue>();

            foreach (SiteDataSet.ProvinceRow province in provinceTable) {
                string id = province.Id.ToString();
                string name = province.Name;
                values.Add(new CascadingDropDownNameValue(name, id));
            }
            return values.ToArray();
        }
    }

    我们先看下方法的签名,第2个参数category即是我们在CascadingDropDown中定义的category属性的值,它传到了这里供我们做一些处理。第1个参数以“Category:Id”的形式包含父下拉框的category值和id值,因为省份为顶级下拉框,所以此时knownCategoryValues为空字符串。返回值为一个CascadingDropDownNameValue数组,其实就是一个键/值对,用于返回给客户端进行绑定。

    方法的内容我觉得不需要有任何解释,都是最平常不过的强类型DataSet的基本操作。需要注意的是Thread.Sleep(1000),这里是为了模拟网络延迟的效果,以让控件能够显示出LoadingText的效果。

    接下来,我们再看剩下两个方法GetCity和GetStreet方法的实现:

    [OperationContract]
    public CascadingDropDownNameValue[] GetCity(string knownCategoryValues, string category)
    {
        Thread.Sleep(1000);

        StringDictionary keyValue =
            CascadingDropDown.ParseKnownCategoryValuesString(knownCategoryValues);

        int provinceId = Convert.ToInt32(keyValue["Province"]);
       
        SiteDataSetTableAdapters.CityTableAdapter cityAdapter =
            new SiteDataSetTableAdapters.CityTableAdapter();

        SiteDataSet.CityDataTable cityTable = cityAdapter.GetCityByProvinceId(provinceId);
        List<CascadingDropDownNameValue> values = new List<CascadingDropDownNameValue>();

        foreach (SiteDataSet.CityRow city in cityTable) {
            string id = city.Id.ToString();
            string name = city.Name;
            values.Add(new CascadingDropDownNameValue(name, id));
        }
       
        return values.ToArray();
    }


    [OperationContract]
    public CascadingDropDownNameValue[] GetStreet(string knownCategoryValues, string category) {
        Thread.Sleep(1000);

        StringDictionary keyValue =
            CascadingDropDown.ParseKnownCategoryValuesString(knownCategoryValues);

        int cityId = Convert.ToInt32(keyValue["City"]);

        SiteDataSetTableAdapters.StreetTableAdapter streetAdapter =
            new SiteDataSetTableAdapters.StreetTableAdapter();

        SiteDataSet.StreetDataTable streetTable = streetAdapter.GetStreetByCityId(cityId);
        List<CascadingDropDownNameValue> values = new List<CascadingDropDownNameValue>();

        foreach (SiteDataSet.StreetRow street in streetTable) {
            string id = street.Id.ToString();
            string name = street.Name;
            values.Add(new CascadingDropDownNameValue(name, id));
        }

        return values.ToArray();
    }

    这两个方法是完全类似的,所以我们仅就GetCity()方法说明。假如我们此刻在页面选择了省份,为“陕西”,且它的id为“2”,那么按照上面的说明,knownCategoryValues包含的值为“Province:2”,它含有了父控件的category值和选中的id值,通过它我们就可以获得选中的省份id。我们并不需要手动去处理这个字符串,比如使用Split()方法,而可以使用CascadingDropDown.ParseKnownCategoryValuesString静态方法来完成,如上面的代码所示。

    在获得了省份id之后,我们可以调用DataAdapter上的方法,来根据省份id获得其下所包含的城市,这些代码大家应该已经很熟悉了。

    好了,现在一切已经就绪,大家可以打开页面看一下实际的效果了:

    总结

    在这篇文章中,我们又实现了Web中的一个常见功能:级联式下拉框。我们先创建了范例数据库,以及一个强类型DataSet用作数据访问。随后对Web页面进行了布局,设置了控件属性。最后编写了WCF服务,获取客户端所需要的数据。

    感谢阅读,希望这篇文章能给你带来帮助!

  • 相关阅读:
    Vjios P1736 铺地毯【暴力,思维】
    Vijos P1116 一元三次方程求解【多解,暴力,二分】
    Vijos P1131 最小公倍数和最大公约数问题【暴力】
    Vijos P1786 质因数分解【暴力】
    C++课程设计类作业4
    析构函数的用法【简单理论讲解】
    C++课程设计类作业3
    最短路径Floyd算法【图文详解】
    HDU 1874 畅通工程续【Floyd算法实现】
    C++课程设计类作业2
  • 原文地址:https://www.cnblogs.com/JimmyZhang/p/1327710.html
Copyright © 2020-2023  润新知