【问题标题】:How do I cast a VSTO Globals.ThisAddIn.Application.ActiveDocument to an OpenXml WordprocessingDocument on the fly?如何即时将 VSTO Globals.ThisAddIn.Application.ActiveDocument 转换为 OpenXml WordprocessingDocument?
【发布时间】:2019-07-17 15:54:09
【问题描述】:

我有一个应用程序可以将文档导出为 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。使用调试器,即使 Count 属性显示 3,我也可以看到第 3 列不存在(注意:我观察到 Column 索引是从 1 开始的,而不是从零开始的)。

我是否需要在加载项中动态地将 Word 文档转换为 OpenXml 文档并使用 OpenXml Table 类成功访问表格?

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

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

它抛出这个异常:

System.InvalidCastException。无法转换类型的 COM 对象 'Microsoft.Office.Interop.Word.DocumentClass' 到类类型 'DocumentFormat.OpenXml.Packaging.WordprocessingDocument'。实例 表示 COM 组件的类型不能转换为执行以下操作的类型 不代表 COM 组件;但是它们可以转换为接口 只要底层 COM 组件支持 QueryInterface 调用 用于接口的 IID。

我能否/如何将 Globals.ThisAddIn.Application.ActiveDocument 在我的 VSTO 加载项中动态转换为 OpenXml WordprocessingDocument?

代码

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 

【问题讨论】:

  • 我想也许你已经指出了我的假设的谬误,即这不是OpenXml 文档,而是WordProcessingML 文档。根据 Eugene 的回答,我尝试使用“Open XML SDK 2.5 Productivity Tool”打开 xml 文件,它说文档已损坏。这是有道理的,它不是OpenXml :-)。您不会相信这个 XML 文件中的表结构是多么复杂。 I think your gut feeling is accurate。我可以避免捕获和忽略异常,因为我只需要定位和修改某些单元格中的文本。

标签: casting ms-word vsto openxml word-interop


【解决方案1】:

没有直接演员。这些对象彼此不相关。您需要保存文档,然后使用 Open XML SDK 打开保存的文件。请参阅Welcome to the Open XML SDK 2.5 for Office 了解更多信息。

【讨论】:

    【解决方案2】:

    虽然 Eugene 说你不能施放是对的

    • Microsoft.Office.Interop.Word.Document

    到一个

    • 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 中重新打开文档

    对样式、编号等进行全面转换。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-31
      • 1970-01-01
      • 1970-01-01
      • 2016-10-03
      • 1970-01-01
      相关资源
      最近更新 更多