C#调用OpenXml操作word文档的基本用法介绍

 更新时间:2026年05月29日 09:14:39   作者:gc_2299  
本文详细介绍了使用图形类Drawing将图表和数据保存到Word文档的方法,强调了GraphicData类型的关键作用并展示了具体代码实现和运行效果,有需要的小伙伴可以了解下

使用图形类Drawing不仅可以将图片保存到word文档,还能将图表、表格等数据保存到word文档,关键就在于其子类型GraphicData支持关联多种类型的图形数据,本文记录以嵌入式布局方式向word中插入图表的基本用法。

主要代码如下所示:

using A = DocumentFormat.OpenXml.Drawing;
using C=DocumentFormat.OpenXml.Drawing.Charts;
using DW = DocumentFormat.OpenXml.Drawing.Wordprocessing;

if(cbInsertChart.Checked)
{
    ChartPart chartPart = mainPart.AddNewPart<ChartPart>();
    string chartPartId = mainPart.GetIdOfPart(chartPart);

    Drawing drawing = CreateChartDrawing(chartPartId, chartPart);
    run.Append(drawing);
}

public Drawing CreateChartDrawing(string relationshipId, ChartPart chartPart)
{
    var chartSpace = new C.ChartSpace();
    chartSpace.AddNamespaceDeclaration("c", "http://schemas.openxmlformats.org/drawingml/2006/chart");
    chartSpace.AddNamespaceDeclaration("r", "http://schemas.openxmlformats.org/officeDocument/2006/relationships");
    chartSpace.AppendChild(new C.EditingLanguage { Val = "zh-CN" });

    var chart = new C.Chart();
    chart.AppendChild(new C.AutoTitleDeleted { Val = true });

    var plotArea = new C.PlotArea();

    var barChart = new C.BarChart(
        new C.BarDirection { Val = C.BarDirectionValues.Column },
        new C.BarGrouping { Val = C.BarGroupingValues.Clustered },
        new C.VaryColors { Val = true }
    );

    var series = new C.BarChartSeries(
        new C.Index { Val = 0 },
        new C.Order { Val = 0 },
        new C.SeriesText(new C.NumericValue("测试数据"))
    );

    series.AppendChild(new C.CategoryAxisData(
        new C.StringLiteral(
            new C.PointCount { Val = 4 },
            new C.StringPoint { Index = 0, NumericValue = new C.NumericValue("A") },
            new C.StringPoint { Index = 1, NumericValue = new C.NumericValue("B") },
            new C.StringPoint { Index = 2, NumericValue = new C.NumericValue("C") },
            new C.StringPoint { Index = 3, NumericValue = new C.NumericValue("D") }
        )
    ));

    series.AppendChild(new C.Values(
        new C.NumberLiteral(
            new C.PointCount { Val = 4 },
            new C.NumericPoint { Index = 0, NumericValue = new C.NumericValue("10") },
            new C.NumericPoint { Index = 1, NumericValue = new C.NumericValue("20") },
            new C.NumericPoint { Index = 2, NumericValue = new C.NumericValue("30") },
            new C.NumericPoint { Index = 3, NumericValue = new C.NumericValue("40") }
        )
    ));

    barChart.AppendChild(series);

    // 坐标轴
    var catAxis = new C.CategoryAxis(
        new C.AxisId { Val = 100 },
        new C.Scaling(new C.Orientation { Val = C.OrientationValues.MinMax }),
        new C.AxisPosition { Val = C.AxisPositionValues.Bottom },
        new C.CrossingAxis { Val = 200 },
        new C.Crosses { Val = C.CrossesValues.AutoZero },
        new C.TickLabelPosition { Val = C.TickLabelPositionValues.NextTo }
    );

    var valAxis = new C.ValueAxis(
        new C.AxisId { Val = 200 },
        new C.Scaling(new C.Orientation { Val = C.OrientationValues.MinMax }),
        new C.AxisPosition { Val = C.AxisPositionValues.Left },
        new C.CrossingAxis { Val = 100 },
        new C.Crosses { Val = C.CrossesValues.AutoZero },
        new C.TickLabelPosition { Val = C.TickLabelPositionValues.NextTo },
        new C.MajorTickMark { Val = C.TickMarkValues.None },
        new C.MinorTickMark { Val = C.TickMarkValues.None }
    );

    // 让BarChart引用坐标轴ID
    barChart.AppendChild(new C.AxisId { Val = 100 });
    barChart.AppendChild(new C.AxisId { Val = 200 });

    plotArea.Append(barChart, catAxis, valAxis);
    chart.Append(plotArea);

    chartSpace.AppendChild(chart);
    chartSpace.Save(chartPart);

    return new Drawing(
        new DW.Inline(
            new DW.Extent { Cx = 6000000, Cy = 4000000 },
            new DW.EffectExtent { LeftEdge = 0L, TopEdge = 0L, RightEdge = 0L, BottomEdge = 0L },
            new DW.DocProperties { Id = 1, Name = "Chart 1" },
            new DW.NonVisualGraphicFrameDrawingProperties(
                new A.GraphicFrameLocks { NoChangeAspect = true }
            ),
            new A.Graphic(
                new A.GraphicData(
                    new C.ChartReference { Id = relationshipId }
                )
                { Uri = "http://schemas.openxmlformats.org/drawingml/2006/chart" }
            )
        )
    );
}

下面的截图是程序运行效果及word文档的内容截图。

知识扩展:

使用 Microsoft 官方提供的 Open XML SDK,我们可以直接在 C# 代码中创建和修改 Word (.docx) 文件,整个过程不依赖本地安装的 Microsoft Office 软件,非常适合在服务器环境或需要自动化批量处理文档的场景中使用。

核心概念:Open XML SDK 如何表示 Word 文档?

在开始编写代码之前,先了解一下 Open XML SDK 中三个最基本的概念,这会帮助你更好地理解后续的代码。

核心元素作用类比如何操作?
段落 (Paragraph)代表文档中的一个段落,是文本的基本容器像是搭积木时的“块”通常需要配合 ParagraphProperties 来设置样式。
文本运行 (Run)Run 是 Paragraph 内部的子元素,表示一段具有相同格式的连续文本。一块积木上可能有不同的颜色,Run 就像给这块积木的某个部分涂上特定的颜色。想要改变字体、大小、颜色,都是操作 Run 的属性,而不是直接去改文本本身。
文本 (Text)Run 内部真正的文本载体积木本身文本内容就是这个类构造函数里传进去的字符串。

理解了这三个核心元素间的层级关系(Paragraph -> Run -> Text),就能明白如何构建文档的基本内容了。

快速上手:从安装开始

准备环境: 首先创建一个 C# 项目(控制台应用、Web API 等均可)。

安装核心库: 在 Visual Studio 中使用 NuGet 包管理器,搜索并安装 DocumentFormat.OpenXml

引入命名空间: 在需要操作 Word 的代码文件顶部,添加以下 using 语句。

using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using System.Text.RegularExpressions;

文档基本操作:打开、创建与保存

Open XML SDK 的核心是 WordprocessingDocument 类,它代表了一个完整的 Word 文档包。

打开现有文档进行编辑:使用 Open() 方法,第二个参数设为 true 表示以读/写模式打开文件。

// 打开文档路径下文件进行编辑(使用 using 确保资源自动释放)
using (WordprocessingDocument wordDoc = WordprocessingDocument.Open(@"C:\test.docx", true))
{
    // 检查文档对象是否为空,避免后续操作出错
    if (wordDoc == null) throw new ArgumentNullException(nameof(wordDoc));
    // ... 在此处进行编辑操作
}

创建一个新文档:使用 Create() 方法,此时需要显式创建主文档部件。

// 创建一个全新的 Word 文档
using (WordprocessingDocument wordDoc = WordprocessingDocument.Create(@"C:\new.docx", WordprocessingDocumentType.Document))
{
    // 新文档必须手动添加主文档部件,否则 Word 无法打开[reference:10]
    MainDocumentPart mainPart = wordDoc.AddMainDocumentPart();
    // 初始化文档结构:Document -> Body
    mainPart.Document = new Document();
    mainPart.Document.Body = new Body();
    // ... 在此处向 Body 中添加内容
}

注意using 语句会自动处理资源的释放和文档的保存。如果一个新创建的文档没有添加 MainDocumentPart,Word 在打开时会提示文件损坏。

文本操作:添加与格式化

掌握了上面的基础,就可以尝试向文档中添加内容了。

添加普通文本段落

// 获取文档的 Body 对象
Body body = wordDoc.MainDocumentPart.Document.Body;

// 创建一个新段落,必须显式包含 Run 和 Text 子元素[reference:14]
Paragraph newParagraph = new Paragraph(
    new ParagraphProperties(), // 段落属性(如对齐方式)
    new Run(
        new Text("这是新添加的文本内容。")
    )
);

// 将新段落追加到 Body 末尾
body.AppendChild(newParagraph);

注意:Open XML 中构造段落的正确方式是 Paragraph 内必须包含至少一个 Run 元素,Run 内必须包含 Text 元素,缺一不可。

添加带格式的文本(加粗、颜色等)格式设置作用于 Run 层级,通过 RunProperties 类来实现。

// 创建一个带格式的文本运行
Run formattedRun = new Run();

// 1. 设置运行属性
RunProperties runProperties = new RunProperties();
runProperties.Bold = new Bold() { Val = true };          // 加粗
runProperties.Color = new Color() { Val = "FF0000" };    // 红色
runProperties.FontSize = new FontSize() { Val = "28" };  // 字号(28 相当于 14 号)
formattedRun.AppendChild(runProperties);

// 2. 添加文本内容
formattedRun.AppendChild(new Text("这是加粗、红色的文本。"));

表格操作:创建简单表格

在 Open XML SDK 中,操作表格需要更严谨地遵循嵌套结构。其最简结构如下:

Body -> Paragraph -> Table -> TableRow -> TableCell -> Paragraph -> Run -> Text
// 创建一个 3 行 2 列的简单表格
Table table = new Table();

// 1. 添加表格属性(例如设置宽度)
TableProperties tblProp = new TableProperties();
TableWidth tableWidth = new TableWidth() { Width = "5000", Type = TableWidthUnitValues.Dxa };
tblProp.AppendChild(tableWidth);
table.AppendChild(tblProp);

for (int i = 0; i &lt; 3; i++)
{
    TableRow row = new TableRow();
    for (int j = 0; j &lt; 2; j++)
    {
        TableCell cell = new TableCell();
        // **注意**:每个 TableCell 必须包含至少一个 Paragraph 才能显示内容[reference:17]
        cell.AppendChild(new Paragraph(new Run(new Text($"第{i+1}行, 第{j+1}列"))));
        row.AppendChild(cell);
    }
    table.AppendChild(row);
}

// 将表格添加到 Body 中
wordDoc.MainDocumentPart.Document.Body.AppendChild(table);

注意:上文示例中包含更完整的表格样式(如边框)设置,以及合并单元格的实现方式。

图片操作:插入图片到文档

插入图片是稍显复杂的操作,因为它涉及在包中添加新的部件。

// 插入图片的完整过程
// 1. 添加图片部件
// 使用绝对路径读取图片文件流[reference:19]
using (FileStream fs = new FileStream(@"C:\test.png", FileMode.Open))
{
    // AddImagePart 时指定的 ImagePartType 必须严格匹配图片文件类型[reference:20]
    ImagePart imagePart = wordDoc.MainDocumentPart.AddImagePart(ImagePartType.Png);
    imagePart.FeedData(fs);
    string imagePartId = wordDoc.MainDocumentPart.GetIdOfPart(imagePart);

    // 2. 在文档正文中创建引用图片的 XML 结构
    // 此处构造 Drawing 元素的相关代码较长,具体可参考官方文档,实现相对复杂
}

注意:插入图片时,图片文件路径必须是运行时能访问到的绝对路径。完整的 Drawing 元素代码较复杂,建议参考官方详细示例。

读取操作:提取文档内容

Open XML SDK 同样适用于需要从 Word 文档中提取信息的场景。

/// &lt;summary&gt;
/// 遍历文档所有段落,读取其纯文本内容
/// &lt;/summary&gt;
public static List&lt;string&gt; ReadAllParagraphs(string filePath)
{
    List&lt;string&gt; allTexts = new List&lt;string&gt;();

    using (WordprocessingDocument wordDoc = WordprocessingDocument.Open(filePath, false)) // 第二个参数为 false 表示只读
    {
        Body body = wordDoc.MainDocumentPart.Document.Body;
        // 遍历 Body 下所有 Paragraph 元素
        foreach (Paragraph para in body.Elements&lt;Paragraph&gt;())
        {
            // 获取该段落内所有 Run 下的 Text 元素的文本并拼接
            string paragraphText = string.Join("", para.Descendants&lt;Text&gt;().Select(t =&gt; t.Text));
            if (!string.IsNullOrWhiteSpace(paragraphText))
            {
                allTexts.Add(paragraphText);
            }
        }
    }
    return allTexts;
}

注意:对于含有多级表格或复杂嵌套结构的文档,可能需要使用递归或更精确定位的方式来提取特定区域的文本。

到此这篇关于C#调用OpenXml操作word文档的基本用法介绍的文章就介绍到这了,更多相关C# OpenXml操作word内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

最新评论