You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用C# ClosedXML/Aspose制作Excel联动下拉菜单?

用ClosedXML实现三级联动下拉菜单

实现思路

先把country-state-city的映射数据存到一个隐藏工作表,给每个国家对应的州、每个州对应的城市创建命名区域,再通过Excel的INDIRECT函数让子下拉菜单关联父选项的选择结果。

代码示例

using ClosedXML.Excel;
using System.Collections.Generic;
using System.Linq;

// 准备联动数据
var countryStateCity = new Dictionary<string, Dictionary<string, List<string>>>
{
    {
        "中国", new Dictionary<string, List<string>>
        {
            { "北京", new List<string> { "朝阳区", "海淀区", "西城区" } },
            { "上海", new List<string> { "黄浦区", "徐汇区", "浦东新区" } }
        }
    },
    {
        "美国", new Dictionary<string, List<string>>
        {
            { "加利福尼亚州", new List<string> { "洛杉矶", "旧金山", "圣地亚哥" } },
            { "纽约州", new List<string> { "纽约市", "布法罗", "罗切斯特" } }
        }
    }
};

using var workbook = new XLWorkbook();
// 用于存放联动数据的隐藏工作表
var dataSheet = workbook.Worksheets.Add("Data");
dataSheet.Hide();

int row = 1;
// 写入所有国家,并创建对应州的命名区域
foreach (var countryEntry in countryStateCity)
{
    string country = countryEntry.Key;
    dataSheet.Cell(row, 1).Value = country;
    row++;

    // 写入当前国家的所有州,创建对应城市的命名区域
    int stateStartRow = row;
    foreach (var stateEntry in countryEntry.Value)
    {
        string state = stateEntry.Key;
        dataSheet.Cell(row, 2).Value = state;
        row++;

        // 写入当前州的所有城市,创建城市的命名区域
        int cityStartRow = row;
        foreach (var city in stateEntry.Value)
        {
            dataSheet.Cell(row, 3).Value = city;
            row++;
        }
        // 给当前州的城市创建命名区域(替换空格为下划线避免Excel命名报错)
        dataSheet.Range(cityStartRow, 3, row - 1, 3).Name = state.Replace(" ", "_");
    }
    // 给当前国家的州创建命名区域
    dataSheet.Range(stateStartRow, 2, row - 1, 2).Name = country.Replace(" ", "_");
}

// 用户操作的工作表
var mainSheet = workbook.Worksheets.Add("Main");
mainSheet.Cell(1, 1).Value = "Country";
mainSheet.Cell(1, 2).Value = "State";
mainSheet.Cell(1, 3).Value = "City";

// 设置Country列的下拉菜单(A2及以下单元格)
var countryList = countryStateCity.Keys.ToList();
var countryValidation = mainSheet.Range("A2:A100").DataValidation.List();
countryValidation.Style = XlDataValidationStyle.InList;
countryValidation.IgnoreBlanks = true;
countryValidation.Source = $"\"{string.Join(",", countryList)}\"";

// 设置State列的下拉菜单(B2及以下单元格),关联A列的选择
var stateValidation = mainSheet.Range("B2:B100").DataValidation.List();
stateValidation.Style = XlDataValidationStyle.InList;
stateValidation.IgnoreBlanks = true;
// 用INDIRECT引用A列对应单元格的命名区域
stateValidation.Source = "INDIRECT(SUBSTITUTE(A2,\" \",\"_\"))";

// 设置City列的下拉菜单(C2及以下单元格),关联B列的选择
var cityValidation = mainSheet.Range("C2:C100").DataValidation.List();
cityValidation.Style = XlDataValidationStyle.InList;
cityValidation.IgnoreBlanks = true;
cityValidation.Source = "INDIRECT(SUBSTITUTE(B2,\" \",\"_\"))";

// 保存文件
workbook.Save("联动下拉菜单.xlsx");
用Aspose.Cells实现三级联动下拉菜单

实现思路

和ClosedXML逻辑一致:用隐藏工作表存储映射数据,创建命名区域,通过INDIRECT函数关联父选项的选择结果,利用Aspose.Cells的API直接操作数据验证规则。

代码示例

using Aspose.Cells;
using System.Collections.Generic;

// 准备联动数据
var countryStateCity = new Dictionary<string, Dictionary<string, List<string>>>
{
    {
        "中国", new Dictionary<string, List<string>>
        {
            { "北京", new List<string> { "朝阳区", "海淀区", "西城区" } },
            { "上海", new List<string> { "黄浦区", "徐汇区", "浦东新区" } }
        }
    },
    {
        "美国", new Dictionary<string, List<string>>
        {
            { "加利福尼亚州", new List<string> { "洛杉矶", "旧金山", "圣地亚哥" } },
            { "纽约州", new List<string> { "纽约市", "布法罗", "罗切斯特" } }
        }
    }
};

var workbook = new Workbook();
// 隐藏工作表存数据
Worksheet dataSheet = workbook.Worksheets.Add("Data");
dataSheet.IsVisible = false;

int row = 0;
foreach (var countryEntry in countryStateCity)
{
    string country = countryEntry.Key;
    dataSheet.Cells[row, 0].PutValue(country);
    row++;

    int stateStartRow = row;
    foreach (var stateEntry in countryEntry.Value)
    {
        string state = stateEntry.Key;
        dataSheet.Cells[row, 1].PutValue(state);
        row++;

        int cityStartRow = row;
        foreach (var city in stateEntry.Value)
        {
            dataSheet.Cells[row, 2].PutValue(city);
            row++;
        }
        // 创建城市的命名区域
        Name cityName = workbook.Worksheets.Names.Add(state.Replace(" ", "_"));
        cityName.RefersTo = $"=Data!${CellsHelper.ColumnIndexToName(2)}${cityStartRow + 1}:${CellsHelper.ColumnIndexToName(2)}${row}";
    }
    // 创建州的命名区域
    Name stateName = workbook.Worksheets.Names.Add(country.Replace(" ", "_"));
    stateName.RefersTo = $"=Data!${CellsHelper.ColumnIndexToName(1)}${stateStartRow + 1}:${CellsHelper.ColumnIndexToName(1)}${row}";
}

// 用户操作的工作表
Worksheet mainSheet = workbook.Worksheets[0];
mainSheet.Name = "Main";
mainSheet.Cells[0, 0].PutValue("Country");
mainSheet.Cells[0, 1].PutValue("State");
mainSheet.Cells[0, 2].PutValue("City");

// 设置Country列下拉
Validation countryValidation = mainSheet.Validations.Add("A2:A100");
countryValidation.Type = ValidationType.List;
countryValidation.IgnoreBlank = true;
countryValidation.InCellDropDown = true;
// 拼接国家列表作为数据源
string countrySource = string.Join(",", countryStateCity.Keys);
countryValidation.Formula1 = $"\"{countrySource}\"";

// 设置State列下拉,关联A列
Validation stateValidation = mainSheet.Validations.Add("B2:B100");
stateValidation.Type = ValidationType.List;
stateValidation.IgnoreBlank = true;
stateValidation.InCellDropDown = true;
stateValidation.Formula1 = "INDIRECT(SUBSTITUTE(A2,\" \",\"_\"))";

// 设置City列下拉,关联B列
Validation cityValidation = mainSheet.Validations.Add("C2:C100");
cityValidation.Type = ValidationType.List;
cityValidation.IgnoreBlank = true;
cityValidation.InCellDropDown = true;
cityValidation.Formula1 = "INDIRECT(SUBSTITUTE(B2,\" \",\"_\"))";

// 保存文件
workbook.Save("联动下拉菜单_Aspose.xlsx");

注意事项

  • 如果数据包含空格、特殊字符,创建命名区域时要替换成下划线等合法字符,避免Excel识别错误。
  • 隐藏数据工作表是为了防止用户误操作原始映射数据,也可以选择将数据放在主工作表的隐藏列中。

内容的提问来源于stack exchange,提问作者fazal mithani

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 02:13:24