首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> ASP.NET 基礎 >> ASP.NET 基礎

ASP.NET 省市三級聯動 DropDownList 實戰:三大 Ajax 框架完整示例

     編輯:ASP.NET 基礎

前言

在 Web 開發中,「省市區三級聯動」是經典的前端交互場景。本文將透過一個統一的數據模型,分別展示如何使用 ASP.NET 內建 Ajax(UpdatePanel)、jQuery 1.4.1 + ASHX、ExtJS 3.2 + ASHX 三種主流框架實現該功能,並提供完整的後端模型、介面定義與前端頁面代碼,方便開發者快速對比與選型。

一、統一數據模型與業務邏輯層

為了讓三個示例共用同一套數據,我們先建立領域模型與資料存取介面:

1. 實體類別 Area.cs

namespace Downmoon.Framework.Model
{
    public class Area
    {
        public string Area_ID { get; set; }
        public string Area_Name { get; set; }
        public double Area_Order { get; set; }
        public int Area_Layer { get; set; }
        public string Area_FatherID { get; set; }

        public Area() { }
        public Area(string id, string name, double order, int layer, string father)
        {
            Area_ID = id; Area_Name = name; Area_Order = order; Area_Layer = layer; Area_FatherID = father;
        }
    }
}

2. 介面 IArea.cs

namespace Downmoon.Framework.Controllers
{
    public interface IArea
    {
        List<Area> GetAreaList();
        List<Area> GetAreaListFindByParentID(string filterID);
    }
}

3. 實作類別 AreaList.cs(單例模式)

namespace Downmoon.Framework.Controllers
{
    public class AreaList : IArea
    {
        private static AreaList _instance;
        public static AreaList Instance => _instance ??= new AreaList();

        public List<Area> GetAreaList()
        {
            var list = new List<Area>();
            // 省份
            list.Add(new Area("110000", "北京市", 0, 1, "000000"));
            list.Add(new Area("330000", "浙江省", 0, 1, "000000"));
            // 市
            list.Add(new Area("110100", "市轄區", 0, 2, "110000"));
            list.Add(new Area("330100", "杭州市", 0, 2, "330000"));
            list.Add(new Area("330200", "寧波市", 0, 2, "330000"));
            // 區/縣
            list.Add(new Area("110101", "東城區", 0, 3, "110100"));
            list.Add(new Area("110102", "西城區", 0, 3, "110100"));
            list.Add(new Area("330102", "上城區", 0, 3, "330100"));
            list.Add(new Area("330103", "下城區", 0, 3, "330100"));
            list.Add(new Area("330203", "海曙區", 0, 3, "330200"));
            // …其餘資料省略
            return list;
        }

        public List<Area> GetAreaListFindByParentID(string filter)
        {
            return GetAreaList().FindAll(a => a.Area_FatherID == filter);
        }
    }
}

4. 工廠類別 Factory.cs

namespace Downmoon.Framework.Controllers
{
    public class Factory
    {
        public static IArea GetAreaController() => AreaList.Instance;
    }
}

專案結構圖


二、方案一:ASP.NET 內建 Ajax(UpdatePanel)

優點:與 ASP.NET 深度整合,幾乎不寫 JavaScript;缺點:.NET Framework 2.0 需額外配置 web.config,4.0 以上則零配置。

1. Framework 2.0 必備 web.config 片段

<configuration>
  <configSections>
    <sectionGroup name="system.web.extensions" type="System.Web.Configuration.SystemWebExtensionsSectionGroup, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
      <sectionGroup name="scripting" type="System.Web.Configuration.ScriptingSectionGroup, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
        <section name="scriptResourceHandler" type="System.Web.Configuration.ScriptingScriptResourceHandlerSection, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="false" allowDefinition="MachineToApplication"/>
        <sectionGroup name="webServices" type="System.Web.Configuration.ScriptingWebServicesSectionGroup, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
          <section name="jsonSerialization" type="System.Web.Configuration.ScriptingJsonSerializationSection, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="false" allowDefinition="Everywhere"/>
        </sectionGroup>
      </sectionGroup>
    </sectionGroup>
  </configSections>
  <system.web>
    <pages>
      <controls>
        <add tagPrefix="asp" namespace="System.Web.UI" assembly="System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
      </controls>
    </pages>
    <httpHandlers>
      <add verb="*" path="*.asmx" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
      <add verb="GET,HEAD" path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" validate="false"/>
    </httpHandlers>
    <httpModules>
      <add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
    </httpModules>
  </system.web>
</configuration>

2. 前端頁面(Default.aspx)

<asp:ScriptManager ID="ScriptManager1" runat="server" EnablePartialRendering="true" />
<div>
  <asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Conditional">
    <ContentTemplate>
      <table width="500" border="0" cellpadding="0" cellspacing="2">
        <tr>
          <td height="25" bgcolor="#EAEAEA">請選擇省/市/區:</td>
          <td bgcolor="#f7f7f7">
            <asp:DropDownList ID="dpProvince" runat="server" AutoPostBack="true" OnSelectedIndexChanged="dpProvince_SelectedIndexChanged" />
            <asp:DropDownList ID="dpCity" runat="server" AutoPostBack="true" OnSelectedIndexChanged="dpCity_SelectedIndexChanged" />
            <asp:DropDownList ID="dpArea" runat="server" AutoPostBack="false" />
          </td>
        </tr>
        <tr>
          <td height="25" bgcolor="#EAEAEA"></td>
          <td bgcolor="#f7f7f7">
            <asp:UpdateProgress ID="UpdateProgress1" runat="server">
              <ProgressTemplate>正在查詢,請稍候……</ProgressTemplate>
            </asp:UpdateProgress>
          </td>
        </tr>
      </table>
    </ContentTemplate>
    <Triggers>
      <asp:AsyncPostBackTrigger ControlID="dpProvince" EventName="SelectedIndexChanged" />
      <asp:AsyncPostBackTrigger ControlID="dpCity" EventName="SelectedIndexChanged" />
    </Triggers>
  </asp:UpdatePanel>
</div>

3. 後端代碼(Default.aspx.cs 關鍵片段)

protected void Page_Load(object sender, EventArgs e)
{
    if (!IsPostBack) BindProvince();
}

private void BindProvince()
{
    var list = Factory.GetAreaController().GetAreaListFindByParentID("000000");
    dpProvince.DataSource = list; dpProvince.DataTextField = "Area_Name"; dpProvince.DataValueField = "Area_ID"; dpProvince.DataBind();
    dpProvince.Items.Insert(0, new ListItem("請選擇...", ""));
}

protected void dpProvince_SelectedIndexChanged(object sender, EventArgs e)
{
    dpCity.Items.Clear(); dpArea.Items.Clear();
    if (!string.IsNullOrEmpty(dpProvince.SelectedValue))
    {
        var list = Factory.GetAreaController().GetAreaListFindByParentID(dpProvince.SelectedValue);
        dpCity.DataSource = list; dpCity.DataTextField = "Area_Name"; dpCity.DataValueField = "Area_ID"; dpCity.DataBind();
        dpCity.Items.Insert(0, new ListItem("請選擇...", ""));
    }
}

protected void dpCity_SelectedIndexChanged(object sender, EventArgs e)
{
    dpArea.Items.Clear();
    if (!string.IsNullOrEmpty(dpCity.SelectedValue))
    {
        var list = Factory.GetAreaController().GetAreaListFindByParentID(dpCity.SelectedValue);
        dpArea.DataSource = list; dpArea.DataTextField = "Area_Name"; dpArea.DataValueField = "Area_ID"; dpArea.DataBind();
        dpArea.Items.Insert(0, new ListItem("請選擇...", ""));
    }
}

UpdatePanel 效果圖


三、方案二:jQuery 1.4.1 + 通用處理程式

優點:輕量、跨瀏覽器、易於維護,適合現代 ASP.NET 專案。

1. 後端處理程式 AjaxRequest.ashx

public class AjaxRequest : IHttpHandler
{
    public void ProcessRequest(HttpContext context)
    {
        string pid = context.Request["pid"] ?? "000000";
        var list = Factory.GetAreaController().GetAreaListFindByParentID(pid);

        context.Response.ContentType = "application/json";
        context.Response.ContentEncoding = Encoding.UTF8;
        context.Response.Write(ListToJson(list));
    }

    private string ListToJson(List<Area> list)
    {
        var sb = new StringBuilder("[");
        for (int i = 0; i < list.Count; i++)
        {
            sb.Append($"{{\"Area_ID\":\"{list[i].Area_ID}\",\"Area_Name\":\"{list[i].Area_Name}\"}}");
            if (i < list.Count - 1) sb.Append(",");
        }
        sb.Append("]");
        return sb.ToString();
    }
    public bool IsReusable => false;
}

2. 前端頁面(Default.aspx)

<head>
  <script src="Scripts/jquery-1.4.1.js"></script>
  <style>
    #dpCity, #dpArea { display: none; position: relative; }
  </style>
</head>
<body>
  <form id="form1" runat="server">
    <div>
      請選擇省/市/區:
      <asp:DropDownList ID="dpProvince" runat="server" />
      <asp:DropDownList ID="dpCity" runat="server" />
      <asp:DropDownList ID="dpArea" runat="server" />
    </div>
    <script>
      $(function () {
        var $dp1 = $("#dpProvince"), $dp2 = $("#dpCity"), $dp3 = $("select[name$=dpArea]");
        loadAreas("000000", "0");

        $dp1.on("change", function () {
          if (this.value) { loadAreas(this.value, "1"); $dp2.fadeIn(); } else $dp2.fadeOut();
        });
        $dp2.on("change", function () {
          if (this.value) { loadAreas(this.value, "2"); $dp3.fadeIn(); } else $dp3.fadeOut();
        });
      });

      function loadAreas(pid, level) {
        $.getJSON("AjaxRequest.ashx?pid=" + pid, function (data) {
          var $target = level === "0" ? $dp1 : level === "1" ? $dp2 : $dp3;
          $target.empty().append($("").val("").text("請選擇..."));
          $.each(data, function (i, item) {
            $target.append($("").val(item.Area_ID).text(item.Area_Name));
          });
        });
      }
    </script>
  </form>
</body>

jQuery 方案效果圖1 jQuery 方案效果圖2


四、方案三:ExtJS 3.2 + 通用處理程式

優點:UI 元件豐富、適合複雜企業級應用;缺點:學習曲線較陡峭、檔案體積大。

1. 後端處理程式 GetAreaXml.ashx

public class GetAreaXml : IHttpHandler
{
    public void ProcessRequest(HttpContext context)
    {
        string pid = context.Request["pid"] ?? "000000";
        var list = Factory.GetAreaController().GetAreaListFindByParentID(pid);

        context.Response.ContentType = "text/html";
        context.Response.ContentEncoding = Encoding.UTF8;
        context.Response.AddHeader("Cache-Control", "no-cache, must-revalidate");

        var sb = new StringBuilder();
        foreach (var a in list)
            sb.Append($"{{\"Area_Name\":\"{a.Area_Name}\",\"Area_ID\":\"{a.Area_ID}\"}},");
        string json = sb.ToString().TrimEnd(',');
        context.Response.Write("{\"Results\":[" + json + "]}");
    }
    public bool IsReusable => false;
}

2. 前端頁面(Default.aspx)

<head>
  <link rel="stylesheet" href="Scripts/ext/3.2/resources/css/ext-all.css" />
  <script src="Scripts/ext/3.2/adapter/ext/ext-base.js"></script>
  <script src="Scripts/ext/3.2/ext-all.js"></script>
  <script>
    Ext.onReady(function () {
      function GetAreas(parentCtrl, childContainer, url) {
        var select = childContainer.child("select");
        select.dom.options.length = 0;
        if (!parentCtrl.getValue()) { childContainer.hide(); return; }
        select.disabled = true;
        Ext.Ajax.request({
          url: url, params: { pid: parentCtrl.getValue() }, method: 'GET',
          success: function (res) {
            var data = Ext.util.JSON.decode(res.responseText);
            if (data.Results.length > 0) {
              select.dom.options.add(new Option("請選擇", "000000"));
              data.Results.forEach(r => select.dom.options.add(new Option(r.Area_Name, r.Area_ID)));
              select.disabled = false; childContainer.show();
            } else childContainer.hide();
          }
        });
      }
    });
  </script>
</head>
<body>
  <form id="form1" runat="server">
    <table width="500"><tr><td>
      <div style="float:left;line-height:20px;">請選擇</div>
      <asp:DropDownList ID="dropProvince" runat="server" Style="float:left;" />
      <div style="float:left;line-height:20px;">省</div>
      <asp:Panel ID="panelArea" runat="server">
        <asp:DropDownList ID="dropArea" runat="server" Style="float:left;" />
        <div style="float:left;line-height:20px;">市</div>
      </asp:Panel>
      <asp:Panel ID="panelArea2" runat="server">
        <asp:DropDownList ID="dropArea2" runat="server" Style="float:left;" />
        <div style="float:left;line-height:20px;">縣</div>
      </asp:Panel>
    </td></tr></table>
  </form>
</body>

ExtJS 效果圖1 ExtJS 效果圖2


五、總結與選型建議

框架適用場景優點缺點
ASP.NET UpdatePanel傳統 WebForms 專案、快速原型零 JS、全自動局部更新ViewState 大、難以客製化、舊版需繁瑣配置
jQuery + ASHX現代化專案、輕量級、需 SEO輕量、標準 JSON、易除錯、社群龐大需手寫 JS、手動處理 UI 狀態
ExtJS 3.2企業級後台、複雜表單/表格元件完整、資料綁定強、UI 專業學習曲線高、檔案大、授權需注意

實務上建議:新專案優先採用 jQuery / Fetch API + Web API / ASP.NET Core;維護舊版 WebForms 專案時,UpdatePanel 仍是最低成本方案;若團隊已具 ExtJS 經驗且需大量 Grid/Tree/Window,則 ExtJS 能大幅提升開發效率。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved