[编]使用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服务,获取客户端所需要的数据。

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

posted @ 2008-11-05 23:41 Jimmy Zhang 阅读(...) 评论(...) 编辑 收藏