Skip to content

第一个Excel自动化程序

让我们开始编写第一个Excel自动化程序,深入了解Excel对象模型,并掌握基本的Excel操作。

理解Excel核心对象模型

在使用MudTools.OfficeInterop.Excel进行开发之前,我们需要先理解Excel的核心对象模型。这个模型与Excel应用程序的层级结构高度一致,从上到下依次为:

  1. IExcelApplication(Excel应用程序) - 代表整个Excel应用程序实例
  2. IExcelWorkbooks(工作簿集合) - 包含所有打开的工作簿
  3. IExcelWorkbook(工作簿) - 代表单个工作簿文件
  4. IExcelWorksheets、IExcelSheets、IExcelComSheets(工作表集合) - 包含工作簿中的所有工作表
  5. IExcelWorksheet、IExcelComSheet(工作表) - 代表单个工作表
  6. IExcelRange(单元格区域) - 代表工作表中的单元格或单元格区域

这种层级结构反映了Excel的实际组织方式,理解这个模型对于有效地使用MudTools.OfficeInterop.Excel至关重要。

工作表集合接口详解

在Excel对象模型中,有三个不同的接口用于表示工作表集合:IExcelWorksheetsIExcelSheetsIExcelComSheets。它们之间存在继承关系,各自有不同的用途:

  • IExcelComSheets是最基础的接口,定义了工作表集合的通用操作,如添加、删除、查找工作表等。
  • IExcelSheets继承自IExcelComSheets,提供了更丰富的操作方法,如复制、移动整个工作表集合等。
  • IExcelWorksheets也继承自IExcelComSheets,专门用于处理普通工作表(不包括图表工作表),提供了专门针对普通工作表的操作方法。

在实际使用中,如果你只需要操作普通工作表,应该使用IExcelWorksheets接口;如果需要处理包括图表工作表在内的所有工作表类型,应该使用IExcelSheets接口。

工作表接口详解

同样,对于工作表本身,也有两个不同的接口:

  • IExcelComSheet是基础接口,定义了所有工作表类型(包括普通工作表和图表工作表)的通用属性和方法。
  • IExcelWorksheet继承自IExcelComSheet,专门用于操作普通工作表,提供了更多针对单元格操作的功能。

IExcelComSheet接口

IExcelComSheet接口是所有工作表类型的基接口,定义了工作表的基本属性和方法,适用于所有类型的工作表,包括普通工作表、图表工作表等。该接口主要包含以下功能:

  1. 基本属性

    • Name:获取或设置工作表的名称
    • Index:获取工作表在工作簿中的索引位置
    • Visible:获取或设置工作表的可见性状态
    • IsProtected:获取工作表是否被保护
    • ParentWorkbook:获取工作表所在的父工作簿对象
  2. 基本操作

  3. 保护相关

IExcelWorksheet接口

IExcelWorksheet接口继承自IExcelComSheet,专门用于操作普通工作表,提供了更多针对单元格操作的功能。该接口主要包含以下功能:

  1. 单元格访问

    • Cells:获取工作表中的所有单元格
    • Range():获取指定范围的单元格区域
    • 索引器:通过行列索引或地址字符串访问特定单元格
  2. 区域操作

  3. 格式设置

  4. 高级功能

当你需要操作单元格、设置格式或进行数据处理时,应该使用IExcelWorksheet接口。如果你只需要进行工作表级别的操作(如重命名、移动、复制等),可以使用IExcelComSheet接口。

典型应用场景:模板化报告初始化

在实际业务中,我们经常需要定期创建结构相同但数据不同的报告。例如,财务部门可能需要每周生成销售报告,这些报告具有相同的格式、表头和公式,但包含不同的数据。

在这种场景下,我们可以编写一个程序来自动化生成一个包含固定表头、公式和格式的Excel文件作为基础模板,然后由业务人员在此基础上填写或导入数据。

代码实战:创建、保存和退出Excel

让我们通过一个完整的示例来演示如何使用MudTools.OfficeInterop.Excel创建、操作和保存Excel文件。

csharp
using MudTools.OfficeInterop;
using System;

namespace ExcelAutomationDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("开始创建Excel自动化程序...");
            
            try
            {
                // 1. 创建Excel应用程序实例
                // 使用using语句确保资源正确释放
                using var excelApp = ExcelFactory.BlankWorkbook();
                
                // 设置Excel应用程序可见性(可选)
                excelApp.Visible = true;
                
                // 禁用警告对话框,避免在保存等操作时弹出提示
                excelApp.DisplayAlerts = false;
                
                // 2. 获取活动工作簿和工作表
                var workbook = excelApp.ActiveWorkbook;
                var worksheet = excelApp.ActiveSheetWrap;
                
                // 3. 在单元格中写入内容
                // 添加表头
                worksheet.Cells[1, 1].Value = "销售报告";
                worksheet.Cells[2, 1].Value = "日期";
                worksheet.Cells[2, 2].Value = "产品名称";
                worksheet.Cells[2, 3].Value = "销售数量";
                worksheet.Cells[2, 4].Value = "单价";
                worksheet.Cells[2, 5].Value = "总金额";
                
                // 添加示例数据
                worksheet.Cells[3, 1].Value = DateTime.Now.AddDays(-2).ToShortDateString();
                worksheet.Cells[3, 2].Value = "产品A";
                worksheet.Cells[3, 3].Value = 100;
                worksheet.Cells[3, 4].Value = 25.50;
                worksheet.Cells[3, 5].Value = "=C3*D3";
                
                worksheet.Cells[4, 1].Value = DateTime.Now.AddDays(-1).ToShortDateString();
                worksheet.Cells[4, 2].Value = "产品B";
                worksheet.Cells[4, 3].Value = 80;
                worksheet.Cells[4, 4].Value = 30.00;
                worksheet.Cells[4, 5].Value = "=C4*D4";
                
                worksheet.Cells[5, 1].Value = DateTime.Now.ToShortDateString();
                worksheet.Cells[5, 2].Value = "产品C";
                worksheet.Cells[5, 3].Value = 120;
                worksheet.Cells[5, 4].Value = 20.00;
                worksheet.Cells[5, 5].Value = "=C5*D5";
                
                // 添加汇总行
                worksheet.Cells[6, 4].Value = "总计";
                worksheet.Cells[6, 5].Value = "=SUM(E3:E5)";
                
                // 4. 设置格式
                // 标题格式
                worksheet.Cells[1, 1].Font.Bold = true;
                worksheet.Cells[1, 1].Font.Size = 16;
                worksheet.Cells[1, 1].Font.Color = System.Drawing.Color.Blue;
                
                // 表头格式
                var headerRange = worksheet.Range("A2", "E2");
                headerRange.Font.Bold = true;
                headerRange.Interior.Color = System.Drawing.Color.LightGray;
                
                // 数据区域边框
                var dataRange = worksheet.Range("A2", "E6");
                dataRange.Borders.LineStyle = XlLineStyle.xlContinuous;
                dataRange.Borders.Weight = XlBorderWeight.xlThin;
                
                // 数字格式
                worksheet.Range("C3", "C6").NumberFormat = "0";
                worksheet.Range("D3", "E6").NumberFormat = "¥#,##0.00";
                
                // 自动调整列宽
                worksheet.Columns.AutoFit();
                
                // 5. 保存工作簿
                string fileName = $"销售报告_{DateTime.Now:yyyyMMdd}.xlsx";
                workbook.SaveAs(fileName);
                
                Console.WriteLine($"Excel文件已保存为: {fileName}");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"操作失败: {ex.Message}");
                Console.WriteLine("请确保已正确安装Microsoft Excel并配置了项目引用。");
            }
            
            Console.WriteLine("程序执行完毕,按任意键退出...");
            Console.ReadKey();
        }
    }
}

代码解析

让我们逐步分析上面的代码:

1. 创建Excel应用程序实例

csharp
using var excelApp = ExcelFactory.BlankWorkbook();

使用ExcelFactory.BlankWorkbook()方法创建一个新的Excel应用程序实例和一个空白工作簿。使用using语句确保在程序结束时自动释放资源。

2. 设置应用程序属性

csharp
excelApp.Visible = true;
excelApp.DisplayAlerts = false;
  • Visible属性控制Excel应用程序窗口是否可见
  • DisplayAlerts属性控制是否显示警告对话框

3. 获取工作簿和工作表

csharp
var workbook = excelApp.ActiveWorkbook;
var worksheet = excelApp.ActiveSheetWrap;

通过ActiveWorkbookActiveSheetWrap属性获取当前活动的工作簿和工作表。

4. 理解ActiveSheet与ActiveSheetWrap的区别

IExcelApplication接口中,有两个获取活动工作表的属性:ActiveSheetActiveSheetWrap。它们之间有重要区别:

  • ActiveSheet返回IExcelComSheet接口类型,这是一个基础接口,包含了所有工作表类型(如普通工作表和图表工作表)的通用方法和属性。
  • ActiveSheetWrap返回IExcelWorksheet接口类型,这是IExcelComSheet的派生接口,专门用于操作普通工作表,提供了更多针对单元格操作的功能。

在大多数情况下,如果你需要操作单元格、设置格式或进行数据处理,应该使用ActiveSheetWrap属性。如果你只需要进行工作表级别的操作(如重命名、移动、复制等),可以使用ActiveSheet属性。

5. 写入数据

csharp
worksheet.Cells[1, 1].Value = "销售报告";

使用Cells属性访问特定单元格,并通过Value属性设置单元格的值。

6. 设置格式

csharp
worksheet.Cells[1, 1].Font.Bold = true;
worksheet.Cells[1, 1].Font.Size = 16;

通过Font属性设置字体格式。

7. 保存文件

csharp
workbook.SaveAs(fileName);

使用SaveAs方法将工作簿保存到指定文件。

重要概念:COM对象生命周期管理

在使用MudTools.OfficeInterop.Excel(或任何COM Interop库)时,正确管理COM对象的生命周期非常重要。不当的资源管理可能导致以下问题:

  1. 内存泄漏 - Excel进程在程序结束后仍然运行
  2. 资源耗尽 - 多次运行程序后系统性能下降
  3. 文件锁定 - Excel文件无法被其他程序访问

最佳实践

  1. 使用using语句 - 确保IDisposable对象被正确释放
  2. 避免过早释放 - 不要手动调用Marshal.FinalReleaseComObject
  3. 让垃圾回收器工作 - 依赖.NET的自动内存管理

虽然在一些传统代码中你可能会看到类似这样的"经典"用法:

csharp
// 不推荐的做法
try
{
    // ... Excel操作代码 ...
}
finally
{
    // 强制释放COM对象(不推荐)
    Marshal.FinalReleaseComObject(worksheet);
    Marshal.FinalReleaseComObject(workbook);
    Marshal.FinalReleaseComObject(excelApp);
    GC.Collect();
    GC.WaitForPendingFinalizers();
}

但在现代.NET开发中,特别是使用MudTools.OfficeInterop.Excel时,推荐的做法是依赖using语句和.NET的自动资源管理:

csharp
// 推荐的做法
using var excelApp = ExcelFactory.BlankWorkbook();
// ... Excel操作代码 ...
// 资源会自动释放

这种方式更加简洁、安全,并且符合现代.NET开发的最佳实践。

总结

本文主要有以下关键知识点:

  1. Excel对象模型 - 理解了从Application到Range的层级结构,特别是工作表集合和工作表接口的差异
  2. 基本操作 - 学会了如何创建、写入、格式化和保存Excel文件
  3. 属性差异 - 理解了ActiveSheetActiveSheetWrap属性的区别及使用场景
  4. 资源管理 - 了解了COM对象生命周期管理的重要性
  5. 实际应用 - 通过模板化报告初始化场景,看到了Excel自动化在实际业务中的价值