如何即时将 VSTO Globals.ThisAddIn.Application.ActiveDocument 转换为 OpenXml WordprocessingDocument?
How do I cast a VSTO Globals.ThisAddIn.Application.ActiveDocument to an OpenXml WordprocessingDocument on the fly?
我有一个应用程序可以将文档导出为 XML 文件。导出的文档是 OpenXml
格式,并且是 recognized/editable 使用 Word(见下面的注释 1)。该文档包含一个相当复杂的 table 结构,其中 "top" table 有几个单元格,每个单元格包含一个子 table。我的任务是编写一个 VSTO 加载项,为用户提供一个按钮。当用户打开其中一个 XML 文件并单击按钮时,加载项会在 "top" table 和子 table 中定位和操作文本。
我的原始代码(参见下面的 "Code")使用 Microsoft.Office.Interop.Word.Table class 来定位 "top" table 和子 [=59] 中的每个单元格=]秒。
当我的代码开始抛出异常时,事情变得很奇怪,因为在其中一个 table 上,Column.Count 属性 显示 3,但使用 [=15= 访问单元格]扔了The requested member of the collection does not exist
。使用调试器,即使计数 属性 显示 3,我也可以看到第 3 列不存在(注意:我观察到列索引是基于 1 的,而不是基于零的)。
我是否需要在加载项中即时将 Word 文档转换为 OpenXml 文档并使用 OpenXml Table class 才能成功访问 table?
认为这就是答案,我安装了 Open XML Package Editor for Modern Visual Studios
并添加了对 DocumentFormat.OpenXml
和 Windows.Base
的引用。然而,当我做演员时:
WordprocessingDocument doc = (WordprocessingDocument)Globals.ThisAddIn.Application.ActiveDocument;
它抛出这个异常:
System.InvalidCastException. Unable to cast COM object of type
'Microsoft.Office.Interop.Word.DocumentClass' to class type
'DocumentFormat.OpenXml.Packaging.WordprocessingDocument'. Instances
of types that represent COM components cannot be cast to types that do
not represent COM components; however they can be cast to interfaces
as long as the underlying COM component supports QueryInterface calls
for the IID of the interface.
我可以 I/How 在我的 VSTO 加载项中动态地将 Globals.ThisAddIn.Application.ActiveDocument 转换为 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++));
}
注一
根据对文件序言的检查,我假设该文档是 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 打开保存的文件。有关详细信息,请参阅 Welcome to the Open XML SDK 2.5 for Office。
虽然 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
。虽然它显然足以转换主要文档部分(包括您的表格)的内容,但您需要:
- 在 Word 中保存并关闭文档;
- 打开、转换和关闭
WordprocessingDocument
;和
- 在 Word 中重新打开文档
对样式、编号等进行全面转换
我有一个应用程序可以将文档导出为 XML 文件。导出的文档是 OpenXml
格式,并且是 recognized/editable 使用 Word(见下面的注释 1)。该文档包含一个相当复杂的 table 结构,其中 "top" table 有几个单元格,每个单元格包含一个子 table。我的任务是编写一个 VSTO 加载项,为用户提供一个按钮。当用户打开其中一个 XML 文件并单击按钮时,加载项会在 "top" table 和子 table 中定位和操作文本。
我的原始代码(参见下面的 "Code")使用 Microsoft.Office.Interop.Word.Table class 来定位 "top" table 和子 [=59] 中的每个单元格=]秒。
当我的代码开始抛出异常时,事情变得很奇怪,因为在其中一个 table 上,Column.Count 属性 显示 3,但使用 [=15= 访问单元格]扔了The requested member of the collection does not exist
。使用调试器,即使计数 属性 显示 3,我也可以看到第 3 列不存在(注意:我观察到列索引是基于 1 的,而不是基于零的)。
我是否需要在加载项中即时将 Word 文档转换为 OpenXml 文档并使用 OpenXml Table class 才能成功访问 table?
认为这就是答案,我安装了 Open XML Package Editor for Modern Visual Studios
并添加了对 DocumentFormat.OpenXml
和 Windows.Base
的引用。然而,当我做演员时:
WordprocessingDocument doc = (WordprocessingDocument)Globals.ThisAddIn.Application.ActiveDocument;
它抛出这个异常:
System.InvalidCastException. Unable to cast COM object of type 'Microsoft.Office.Interop.Word.DocumentClass' to class type 'DocumentFormat.OpenXml.Packaging.WordprocessingDocument'. Instances of types that represent COM components cannot be cast to types that do not represent COM components; however they can be cast to interfaces as long as the underlying COM component supports QueryInterface calls for the IID of the interface.
我可以 I/How 在我的 VSTO 加载项中动态地将 Globals.ThisAddIn.Application.ActiveDocument 转换为 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++));
}
注一
根据对文件序言的检查,我假设该文档是 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 打开保存的文件。有关详细信息,请参阅 Welcome to the Open XML SDK 2.5 for Office。
虽然 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
。虽然它显然足以转换主要文档部分(包括您的表格)的内容,但您需要:
- 在 Word 中保存并关闭文档;
- 打开、转换和关闭
WordprocessingDocument
;和 - 在 Word 中重新打开文档
对样式、编号等进行全面转换