C# 如何在 Excel 中创建下拉列表:3 种常见数据源实现方式

3 阅读9分钟

在员工信息表、订单登记表、项目台账等 Excel 文件中,经常会有一些字段只能填写固定内容,例如部门、任务状态、审批结果或产品类型。

如果完全依赖用户手动输入,同一个选项可能出现不同写法。例如任务状态可能同时出现“进行中”“处理中”或其他类似表达,后续进行筛选、统计或数据导入时就需要额外清洗。

为这些单元格添加下拉列表,可以让用户直接从预设选项中选择,从录入阶段减少数据不一致的问题。

本文将介绍如何使用 C# 为 Excel 单元格创建下拉列表,主要包括以下三种常见场景:

  • 使用固定选项创建下拉列表
  • 使用当前工作表中的单元格区域作为下拉数据源
  • 引用其他工作表中的数据创建下拉列表

安装所需的 Excel 库

本文使用 Spire.XLS for .NET 来创建和修改 Excel 文件。它可以直接操作 XLS 和 XLSX 文件,并支持通过 Excel 数据验证功能创建下拉列表,不需要在运行环境中安装 Microsoft Excel。

可以通过 NuGet Package Manager Console 安装:

Install-Package Spire.XLS

安装完成后,在代码中引入命名空间:

using Spire.Xls;

接下来可以根据下拉选项的数据来源选择不同的实现方式。

使用固定选项创建 Excel 下拉列表

如果下拉列表中的选项数量比较少,而且内容基本不会变化,可以直接在代码中定义一个字符串数组。

例如,在任务管理表中,可以将任务状态限制为以下几种:

  • 未开始
  • 进行中
  • 已完成
  • 已暂停

下面的示例在 Excel 中创建一个简单的任务表,并为 ​​D2​​ 单元格添加状态下拉列表。

using Spire.Xls;

namespace CreateExcelDropdown
{
    class Program
    {
        static void Main(string[] args)
        {
            // 创建 Workbook 对象
            Workbook workbook = new Workbook();

            // 获取第一个工作表
            Worksheet sheet = workbook.Worksheets[0];
            sheet.Name = "任务管理";

            // 添加表头
            sheet.Range["A1"].Text = "任务编号";
            sheet.Range["B1"].Text = "任务名称";
            sheet.Range["C1"].Text = "负责人";
            sheet.Range["D1"].Text = "状态";

            // 添加示例数据
            sheet.Range["A2"].Text = "RW001";
            sheet.Range["B2"].Text = "编制项目实施计划";
            sheet.Range["C2"].Text = "张伟";

            // 定义下拉列表选项
            string[] statusValues =
            {
                "未开始",
                "进行中",
                "已完成",
                "已暂停"
            };

            // 为 D2 单元格设置下拉列表
            sheet.Range["D2"].DataValidation.Values = statusValues;

            // 自动调整列宽
            sheet.AllocatedRange.AutoFitColumns();

            // 保存文件
            workbook.SaveToFile(
                "任务状态下拉列表.xlsx",
                ExcelVersion.Version2016);

            workbook.Dispose();
        }
    }
}

这里真正用于创建下拉列表的是:

sheet.Range["D2"].DataValidation.Values = statusValues;

​DataValidation.Values​​ 可以直接接收字符串数组,并将数组中的内容作为单元格的数据验证选项。

这种方式比较适合:

  • 状态
  • 优先级
  • 是否启用
  • 审批结果
  • 固定类别

如果选项数量较多,或者后续经常需要调整,则不建议将所有值直接写在代码中,可以考虑把数据源放到工作表中。

根据单元格区域创建 Excel 下拉列表

有些下拉选项本身就保存在 Excel 文件中。

例如,在员工信息表中,可以在 ​​F2:F5​​ 区域保存部门列表:

单元格内容
F2销售部
F3财务部
F4技术部
F5人力资源部

然后让员工信息中的“部门”字段直接引用这一区域。

using Spire.Xls;

namespace CreateDropdownFromRange
{
    class Program
    {
        static void Main(string[] args)
        {
            Workbook workbook = new Workbook();

            Worksheet sheet = workbook.Worksheets[0];
            sheet.Name = "员工信息";

            // 创建员工信息表
            sheet.Range["A1"].Text = "员工编号";
            sheet.Range["B1"].Text = "员工姓名";
            sheet.Range["C1"].Text = "所属部门";

            sheet.Range["A2"].Text = "YG001";
            sheet.Range["B2"].Text = "李明";

            // 创建部门数据源
            sheet.Range["F1"].Text = "部门列表";
            sheet.Range["F2"].Text = "销售部";
            sheet.Range["F3"].Text = "财务部";
            sheet.Range["F4"].Text = "技术部";
            sheet.Range["F5"].Text = "人力资源部";

            // 获取部门数据区域
            CellRange departmentRange = sheet.Range["F2:F5"];

            // 将该区域设置为 C2 的下拉数据源
            sheet.Range["C2"].DataValidation.DataRange =
                departmentRange;

            sheet.AllocatedRange.AutoFitColumns();

            workbook.SaveToFile(
                "部门下拉列表.xlsx",
                ExcelVersion.Version2016);

            workbook.Dispose();
        }
    }
}

这里使用的是:

sheet.Range["C2"].DataValidation.DataRange = departmentRange;

与直接设置字符串数组相比,使用单元格区域作为数据源更容易维护。

例如需要增加一个新的部门时,可以直接更新数据源区域,而不必修改每一个下拉列表的选项。

这种方式比较适合下拉数据本身已经保存在 Excel 工作表中的情况。

不过,在正式的业务模板中,把部门、地区、产品类型等辅助数据直接放在业务表右侧,可能会影响工作表的整洁程度。

更常见的做法是将这些基础选项单独放到一个工作表中。

引用其他工作表的数据创建下拉列表

实际项目中,经常会把业务数据和下拉选项分开保存。

例如,一个 Excel 模板中包含两个工作表:

  • ​员工信息​​:保存员工基本信息
  • ​基础数据​​:保存部门、职位、状态等基础选项

这样既方便维护,也不会让辅助数据影响主工作表的展示。

下面创建一个跨工作表引用的下拉列表。

using Spire.Xls;

namespace CreateCrossSheetDropdown
{
    class Program
    {
        static void Main(string[] args)
        {
            Workbook workbook = new Workbook();

            // 获取员工信息工作表
            Worksheet employeeSheet = workbook.Worksheets[0];
            employeeSheet.Name = "员工信息";

            // 新建基础数据工作表
            Worksheet optionsSheet =
                workbook.Worksheets.Add("基础数据");

            // -------------------------
            // 员工信息工作表
            // -------------------------

            employeeSheet.Range["A1"].Text = "员工编号";
            employeeSheet.Range["B1"].Text = "员工姓名";
            employeeSheet.Range["C1"].Text = "所属部门";

            employeeSheet.Range["A2"].Text = "YG001";
            employeeSheet.Range["B2"].Text = "李明";

            // -------------------------
            // 基础数据工作表
            // -------------------------

            optionsSheet.Range["A1"].Text = "部门列表";
            optionsSheet.Range["A2"].Text = "销售部";
            optionsSheet.Range["A3"].Text = "财务部";
            optionsSheet.Range["A4"].Text = "技术部";
            optionsSheet.Range["A5"].Text = "人力资源部";

            // 允许数据验证引用其他工作表中的区域
            workbook.Allow3DRangesInDataValidation = true;

            // 获取部门数据区域
            CellRange departmentRange =
                optionsSheet.Range["A2:A5"];

            // 设置下拉列表的数据源
            employeeSheet.Range["C2"]
                .DataValidation.DataRange = departmentRange;

            employeeSheet.AllocatedRange.AutoFitColumns();
            optionsSheet.AllocatedRange.AutoFitColumns();

            workbook.SaveToFile(
                "跨工作表部门下拉列表.xlsx",
                ExcelVersion.Version2016);

            workbook.Dispose();
        }
    }
}

跨工作表引用时,有一个比较容易忽略的设置:

workbook.Allow3DRangesInDataValidation = true;

它用于允许数据验证引用其他工作表中的区域。

设置完成后,就可以正常指定:

employeeSheet.Range["C2"].DataValidation.DataRange =
    optionsSheet.Range["A2:A5"];

这种方式更适合实际业务模板。例如工作簿可以设计为:

工作簿
│
├── 员工信息
│   ├── 员工编号
│   ├── 员工姓名
│   └── 所属部门 ▼
│
└── 基础数据
    ├── 销售部
    ├── 财务部
    ├── 技术部
    └── 人力资源部

如果不希望普通用户看到辅助数据,还可以根据实际需求隐藏“基础数据”工作表。

为多个单元格批量添加下拉列表

前面的示例只给一个单元格添加了下拉列表,但实际表格通常需要整列或一段区域都使用相同的数据验证规则。

例如,要让 ​​C2:C100​​ 都使用部门下拉列表,可以直接指定整个区域:

employeeSheet.Range["C2:C100"]
    .DataValidation.DataRange = departmentRange;

固定选项也可以使用同样的方式:

string[] statusValues =
{
    "未开始",
    "进行中",
    "已完成",
    "已暂停"
};

sheet.Range["D2:D100"].DataValidation.Values =
    statusValues;

这种方式比逐个遍历单元格设置数据验证更加简洁,也更适合批量生成 Excel 模板。

三种方式怎么选

三种实现方式本质上的区别在于下拉列表的数据来源。

数据来源实现方式适用场景
固定字符串​DataValidation.Values​状态、优先级、审批结果等固定选项
当前工作表区域​DataValidation.DataRange​简单模板或少量辅助数据
其他工作表区域​DataValidation.DataRange​​​ + ​​Allow3DRangesInDataValidation​正式业务模板、集中维护基础选项

如果只有“是 / 否”“启用 / 禁用”这类固定选项,直接使用字符串数组即可。

如果选项经常变化,或者本身来自业务数据,建议使用单元格区域作为数据源。

对于员工模板、订单模板、项目管理表等长期使用的文件,则可以将基础选项统一放在单独的“基础数据”工作表中。

实际开发中的几个注意点

1. 不要把经常变化的选项全部写死在代码中

假设部门列表原来只有:

销售部
财务部
技术部

后来增加:

客户服务部

如果所有选项都硬编码在 C# 中,就需要修改代码并重新部署。

如果这些数据来自数据库、配置文件或后台管理系统,更合适的方式通常是:

  1. 从业务系统读取最新选项
  2. 将选项写入 Excel 的“基础数据”工作表
  3. 让业务单元格的数据验证引用对应区域

这样可以让 Excel 模板和业务数据保持同步。

2. 注意数据源区域是否完整

如果下拉列表的数据源设置为:

A2:A5

后来实际选项已经增加到了 ​​A8​​,但引用范围没有同步修改,新增加的内容就不会出现在下拉列表中。

对于动态数据,可以在生成 Excel 时根据实际数据数量计算区域范围。

例如:

int lastRow = 8;

employeeSheet.Range["C2:C100"]
    .DataValidation.DataRange =
    optionsSheet.Range["A2:A" + lastRow];

这样数据源就可以根据实际记录数量变化。

3. 下拉列表不等于业务数据校验

Excel 数据验证能够减少用户输入错误,但如果最终还要将 Excel 数据导入数据库,后端仍然应该进行必要的数据校验。

例如“所属部门”字段虽然来自下拉列表,但导入时仍可以再次确认该部门是否存在于当前系统的有效部门列表中。

这样可以避免用户通过复制粘贴、外部工具修改文件等方式绕过 Excel 本身的数据验证。

4. 级联下拉列表需要额外设计

如果不同下拉列表之间存在依赖关系,例如:

省份 → 城市
产品类别 → 产品
部门 → 岗位

则普通的固定下拉列表已经不够。

这类场景通常需要结合:

  • 命名区域
  • 数据验证公式
  • ​INDIRECT​​ 等 Excel 公式

来实现级联下拉列表。

因此,在设计 Excel 模板时,最好先判断下拉选项是独立数据,还是存在上下级关系,再选择合适的实现方案。

总结

在 C# 中生成 Excel 模板时,下拉列表是一个很实用的数据验证功能,可以减少手动录入带来的拼写错误和数据格式不一致。

本文介绍了三种常见实现方式:

  • 使用固定字符串创建下拉列表
  • 使用当前工作表中的单元格区域作为数据源
  • 引用其他工作表中的数据创建下拉列表

对于固定且数量较少的选项,直接使用字符串数组最简单;对于需要长期维护或来自业务系统的数据,则更适合将选项写入工作表,再通过数据验证区域进行引用。

在实际项目中,可以根据数据来源和维护方式选择合适的实现方案,从而让生成的 Excel 文件既方便填写,也更利于后续的数据处理和导入。