使用 OpenXML 更新 Word 文档中的图表






4.58/5 (9投票s)
本文包含有关如何在 Word 文档中更新图表的信息。
引言
本文包含有关如何以编程方式使用 Open XML 2.0 更新 Word 文档中的图表的信息。
背景
假设您有一个 Word 文档,您想要使用一些新值来更新一个图表。我在 Google 上搜索了一些关于如何执行此操作的示例,但这些文章总是忽略了一个事实,即您需要再次打开 Word 文档,右键单击图表,打开“编辑数据”,然后关闭电子表格才能更新这些值。此应用程序展示了如何自动完成这一切。
使用代码
在 Word 文档中添加图表
- 启动 Microsoft Word 2007(或更高版本)。
- 导航到“插入”选项卡。
- 单击插图框中的“图表”图标。
- 对于此示例,通过从列模板中选择它来添加一个三维柱形图。
- 这现在将打开一个包含一些默认值的电子表格。关闭此窗口。
- 图表现在将出现在 Word 文档中
- 保存文档并关闭它。
实现细节
要在 Word 文档中更新图表,您需要
- 更新电子表格单元格
- 获取 docx 文件中的图表部分
- 找到与电子表格单元格相对应的元素
- 更新元素
- 保存文档
我在某处找到了 UpdateChart 方法和 InsertCellInWorksheet 方法中的代码并使用了它。这些方法可以通过在 Google 中搜索“图表更新”来找到。然后我创建了 ModifyChartSimplified 和 ModifyChartDetailed 方法来实际更新 Word 文档。 ModifyChartSimplified 和 ModifyChartDetailed 方法执行完全相同的操作,但它们只是说明了您可以在 Word 文档文件中导航 XML 元素的方式。
如果您在 Deflector 工具中打开 Word 文档,然后单击 /word/document.xml 下的“图表”元素,您可以进一步向下钻取,并查看存储图表数据的 XML 数据。
下面我只展示了存储在文档文件中的“系列 1”XML 数据
<c:chartSpace xmlns:c="http://schemas.openxmlformats.org/drawingml/2006/chart"  
xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"  
xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
  <c:date1904 val="1" />
  <c:lang val="en-US" />
  <c:chart>
    <c:view3D>
      <c:perspective val="30"  />
    </c:view3D>
    <c:plotArea>
      <c:layout />
      <c:bar3DChart>
        <c:barDir val="col" />
        <c:grouping  val="clustered" />
        <c:ser>
           <c:idx val="0" />
           <c:order val="0" />
           <c:tx>
            <c:strRef>
              <c:f>Sheet1!$B$1</c:f>
              <c:strCache>
                 <c:ptCount val="1"  />
                 <c:pt idx="0">
                    <c:v>Series  1</c:v>
                 </c:pt>
              </c:strCache>
            </c:strRef>
           </c:tx>
           <c:cat>
             <c:strRef>
               <c:f>Sheet1!$A$2:$A$5</c:f>
               <c:strCache>
                 <c:ptCount val="4"  />
                 <c:pt idx="0">
                    <c:v>Category  1</c:v>
                 </c:pt>
                 <c:pt idx="1">
                     <c:v>Category  2</c:v>
                 </c:pt>
                 <c:pt idx="2">
                    <c:v>Category  3</c:v>
                 </c:pt>
                 <c:pt idx="3">
                    <c:v>Category  4</c:v>
                 </c:pt>
               </c:strCache>
             </c:strRef>
            </c:cat>
            <c:val>
              <c:numRef>
                <c:f>Sheet1!$B$2:$B$5</c:f>
                <c:numCache>
                   <c:formatCode>General</c:formatCode>
                   <c:ptCount val="4"  />
                   <c:pt idx="0">
                     <c:v>4.3</c:v>
                   </c:pt>
                   <c:pt idx="1">
                      <c:v>2.5</c:v>
                   </c:pt>
                   <c:pt idx="2">
                      <c:v>3.5</c:v>
                   </c:pt>
                   <c:pt idx="3">
                      <c:v>4.5</c:v>
                   </c:pt>
                </c:numCache>
              </c:numRef>
            </c:val>
        </c:ser>
如果您查看我的 ModifiedDetailed 方法,您可以看到我基本上钻取了每个元素并获取了下一个元素。 这对于学习 OpenXML 格式来说是一个很好的练习。
private void ModifyChartDetailed
(string  cellColumn, uint intRow, string cellValue, bool  axisValue)
{
  try
  {
    ChartPart  c_p = this.mainDocPart.ChartParts.FirstOrDefault();
    Chart  chart = c_p.ChartSpace.Descendants<Chart>().FirstOrDefault();
    PlotArea  p_c = chart.PlotArea;
    Bar3DChart  b3d = p_c.Descendants<Bar3DChart>().FirstOrDefault();
    BarChartSeries  bs1 = b3d.Descendants<BarChartSeries>().Where(s  => 
    string.Compare(s.InnerText, "Sheet1!$" + cellColumn + "$1", true)  > 0).First();
    if  (axisValue)
    {
      CategoryAxisData  v1 = bs1.Descendants<CategoryAxisData>().FirstOrDefault();
      StringReference  sr = v1.Descendants<StringReference>().First();
      StringCache  sc = sr.Descendants<StringCache>().First();
      StringPoint  sp = sc.Descendants<StringPoint>().First();
      NumericValue  nv = sp.Descendants<NumericValue>().First();
      nv.Text = cellValue;
    }
    else
    {
      DocumentFormat.OpenXml.Drawing.Charts.Values v1 =  bs1.Descendants;
      DocumentFormat.OpenXml.Drawing.Charts.Values>().FirstOrDefault();
      NumberReference  nr = v1.Descendants<NumberReference>().First();
      NumberingCache  nc = nr.Descendants<NumberingCache>().First();
      NumericPoint  np = nc.Descendants<NumericPoint>().ElementAt((int)intRow - 2);
      NumericValue  nv = np.Descendants<NumericValue>().First();
      nv.Text = cellValue;
     }
  }
  catch
  {
    // Chart Element is not in a recognizable format. 
    // Most likely the defined Chart is  incorrect. Ignore the chart creation.
    return;
   }
  }
在 ModifyChartSimplified 中,我只转到我知道我需要的元素并直接更新它们。
private void ModifyChartSimplified
(string  cellColumn, uint intRow, string cellValue, bool  axisValue)
{
  try
  {
    ChartPart  c_p = this.mainDocPart.ChartParts.FirstOrDefault();
    BarChartSeries  bs1 = c_p.ChartSpace.Descendants<BarChartSeries>().Where
    (s  => string.Compare(s.InnerText, "Sheet1!$" + 
		cellColumn + "$1", true)  > 0).First();
    if  (axisValue)
    {
       NumericValue  nv1 = bs1.Descendants<NumericValue>().First();
       nv1.Text = cellValue;
     }
     else
     {
       DocumentFormat.OpenXml.Drawing.Charts.Values  v1 = 
       bs1.Descendants<DocumentFormat.OpenXml.Drawing.Charts.Values>().FirstOrDefault();
       NumericPoint  np = v1.Descendants<NumericPoint>().ElementAt((int)intRow - 2);
       NumericValue  nv = np.Descendants<NumericValue>().First();
       nv.Text = cellValue;
     }
  }
  catch
  {
     // Chart Element is not in a recognizable format. 
     // Most likely the defined Chart is  incorrect. Ignore the chart creation.
     return;
  }
}
关注点
使用 Deflector 工具确实帮助我很多,让我了解 OpenXML SDK 的工作原理。一开始它可能看起来令人难以承受,但它值得研究。
请随时评论本文以进行改进和批评。当我在 Google 上搜索如何在图像占位符中插入图片时,我发现许多文章使用直接 XML 来查找更新的位置。我认为一定有一种更简单的方法,只需使用 OpenXML 本身,令我欣慰的是,确实如此。
历史
- V1 - 2011 年 4 月 28 日 - 创建并发布应用程序


