如何将 VSTO Globals.ThisAddIn.Application.ActiveDocument 动态转换为



我有一个应用程序,它提供了将文档导出为XML文件的功能。导出的文档采用OpenXml格式,可使用 Word 识别/编辑(请参阅下面的注释 1)。该文档包含一个相当复杂的表结构,其中"顶部"表有几个单元格,每个单元格都包含一个子表。我的任务是编写为用户提供按钮的 VSTO 外接程序。当用户打开其中一个 XML 文件并单击按钮时,外接程序将查找并操作"顶部"表和子表中的文本。

我的原始代码(请参阅下面的"代码")使用 Microsoft.Office.Interop.Word.Table 类来定位"顶部"表和子表中的每个单元格。

当我的代码开始引发异常时,事情变得很奇怪,因为在其中一个表上,Column.Count 属性显示 3,但使用objTable.Cell(row, column)访问单元格会引发The requested member of the collection does not exist。使用调试器,我可以看到列 3 不存在,即使认为 Count 属性显示 3(注意:我观察到列索引是从 1 开始的,而不是从零开始的)。

我是否需要在加载项中动态将 Word 文档强制转换为 OpenXml 文档,并使用 OpenXml 表类成功访问这些表?

以为这就是答案,我安装了Open XML Package Editor for Modern Visual Studios并添加了DocumentFormat.OpenXmlWindows.Base的引用。但是,当我做演员表时:

WordprocessingDocument doc = (WordprocessingDocument)Globals.ThisAddIn.Application.ActiveDocument;

它抛出以下异常:

系统。无效转换异常。无法强制转换类型的 COM 对象 "Microsoft.Office.Interop.Word.DocumentClass"到类类型 'DocumentFormat.OpenXml.Packaging.WordprocessingDocument'.实例 表示 COM 组件的类型不能强制转换为具有以下条件的类型 不表示 COM 组件;但是它们可以强制转换为接口 只要基础 COM 组件支持查询接口调用 用于接口的 IID。

我/如何在我的 VSTO 加载项中动态地将 Globals.ThisAddIn.Application.ActiveDocument 强制转换为 OpenXml 字处理文档吗?

法典

Microsoft.Office.Interop.Word.Range rngDoc = Globals.ThisAddIn.Application.ActiveDocument.Content;
int i = 1;
foreach (Microsoft.Office.Interop.Word.Table objTable in rngDoc.Tables)
{
DumpTable(objTable: objTable, tableNumber: i++, childTableNumber: 0);
}

private void DumpTable(Microsoft.Office.Interop.Word.Table objTable, int tableNumber, int childTableNumber)
{
for (int row = 1; row <= objTable.Rows.Count; row++)
{
for (int column = 1; column <= objTable.Columns.Count; column++)
{
Cell cell = null;
try
{
cell = objTable.Cell(row, column);
Debug.WriteLine(string.Format("Table {0}.{1}. row={2}. column={3}. cell text={4}", tableNumber, childTableNumber, row, column, cell.Range.Text));
}
catch (Exception e)
{
Debug.WriteLine(string.Format("Table {0}.{1}. row={2} + column={3} threw exception: {4}", tableNumber, childTableNumber, row, column, e.Message));
}
}
}
Debug.WriteLine(string.Format("Table {0}.{1}. Start Child Tables", tableNumber, childTableNumber));
foreach (Microsoft.Office.Interop.Word.Table child_tb in objTable.Tables)
{
DumpTable(child_tb, tableNumber, childTableNumber + 1);
}
Debug.WriteLine(string.Format("Table {0}.{1}. End Child Tables", tableNumber, childTableNumber++));
}

注1

我假设该文档是基于对文件前导码的检查的 OpenXml 格式(见xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006")

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<?mso-application progid="Word.Document"?>
<w:wordDocument xmlns:aml="http://schemas.microsoft.com/aml/2001/core" 
xmlns:wpc="http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas" 
xmlns:dt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882" 
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
xmlns:o="urn:schemas-microsoft-com:office:office" 
xmlns:v="urn:schemas-microsoft-com:vml" 
xmlns:w10="urn:schemas-microsoft-com:office:word" 
xmlns:w="http://schemas.microsoft.com/office/word/2003/wordml" 
xmlns:wx="http://schemas.microsoft.com/office/word/2003/auxHint" 
xmlns:wne="http://schemas.microsoft.com/office/word/2006/wordml" 
xmlns:wsp="http://schemas.microsoft.com/office/word/2003/wordml/sp2" 
xmlns:sl="http://schemas.microsoft.com/schemaLibrary/2003/core" 
xmlns:ns0="http://tempuri.org/AllInOneOctoFBISchema.xsd" 
xmlns:ns1="http://tempuri.org/AllInOneOctoFBIFirstFooterSchema.xsd" 
w:macrosPresent="no" w:embeddedObjPresent="no" 
w:ocxPresent="no" xml:space="preserve"><w:ignoreSubtree 
w:val="http://schemas.microsoft.com/office/word/2003/wordml/sp2"/>
.
.
remainder of file 

没有直接投射。这些对象彼此不相关。您需要保存文档,然后使用 Open XML SDK 打开保存的文件。有关详细信息,请参阅欢迎使用 Open XML SDK 2.5 for Office。

虽然尤金说得对,你不能投

  • Microsoft.Office.Interop.Word.Document

到 a

  • DocumentFormat.OpenXml.Packaging.WordprocessingDocument

有一种方法可以在两者之间进行转换。以下示例对此进行了说明:

// Get hold of a Range that you want to transform, using the Open XML SDK.
// In this example, document.Range() represents the whole document.
Document document = Globals.ThisAddIn.Application.ActiveDocument;
Range range = document.Range();
// Create a WordprocessingDocument reflecting that Range from the Flat OPC
// string returned by the Range.WordOpenXML property.
WordprocessingDocument wordDocument = WordprocessingDocument.FromFlatOpcString(range.WordOpenXML);
// Transform the WordprocessingDocument.
// ...
// Convert the WordprocessingDocument back into a Flat OPC string and insert
// it into the original Range.
range.InsertXML(wordDocument.ToFlatOpcString());

请注意,您不会以这种方式获得 100% 的完整WordprocessingDocument。虽然显然有足够的数据来转换主文档部分(包括表格)的内容,但您需要:

  1. 在Word中保存并关闭文档;
  2. 打开、转换和关闭WordprocessingDocument;以及
  3. 在 Word 中重新打开文档

执行样式、编号等的完整转换。

最新更新