2009-10-23 73 views
4

我能夠使用此代碼訪問書籤我的word文檔中:插入OpenXmlElement字後書籤開放XML SDK

var res = from bm in mainPart.Document.Body.Descendants<BookmarkStart>() 
           where bm.Name == "BookmarkName" 
           select bm; 

現在我要插入此書籤後一個段落和一張桌子。我怎麼做? (示例代碼,將不勝感激)

回答

24

代碼

一旦你有,你可以訪問它的父元素,並在其後添加其他項目的書籤。

using (WordprocessingDocument document = WordprocessingDocument.Open(@"C:\Path\filename.docx", true)) 
{ 
    var mainPart = document.MainDocumentPart; 
    var res = from bm in mainPart.Document.Body.Descendants<BookmarkStart>() 
       where bm.Name == "BookmarkName" 
       select bm; 
    var bookmark = res.SingleOrDefault(); 
    if (bookmark != null) 
    { 
     var parent = bookmark.Parent; // bookmark's parent element 

     // simple paragraph in one declaration 
     //Paragraph newParagraph = new Paragraph(new Run(new Text("Hello, World!"))); 

     // build paragraph piece by piece 
     Text text = new Text("Hello, World!"); 
     Run run = new Run(new RunProperties(new Bold())); 
     run.Append(text); 
     Paragraph newParagraph = new Paragraph(run); 

     // insert after bookmark parent 
     parent.InsertAfterSelf(newParagraph); 

     var table = new Table(
     new TableProperties(
      new TableStyle() { Val = "TableGrid" }, 
      new TableWidth() { Width = 0, Type = TableWidthUnitValues.Auto } 
      ), 
      new TableGrid(
       new GridColumn() { Width = (UInt32Value)1018U }, 
       new GridColumn() { Width = (UInt32Value)3544U }), 
     new TableRow(
      new TableCell(
       new TableCellProperties(
        new TableCellWidth() { Width = 0, Type = TableWidthUnitValues.Auto }), 
       new Paragraph(
        new Run(
         new Text("Category Name")) 
       )), 
      new TableCell(
       new TableCellProperties(
        new TableCellWidth() { Width = 4788, Type = TableWidthUnitValues.Dxa }), 
       new Paragraph(
        new Run(
         new Text("Value")) 
       )) 
     ), 
     new TableRow(
      new TableCell(
       new TableCellProperties(
        new TableCellWidth() { Width = 0, Type = TableWidthUnitValues.Auto }), 
       new Paragraph(
        new Run(
         new Text("C1")) 
       )), 
      new TableCell(
       new TableCellProperties(
        new TableCellWidth() { Width = 0, Type = TableWidthUnitValues.Auto }), 
       new Paragraph(
        new Run(
         new Text("V1")) 
       )) 
     )); 

     // insert after new paragraph 
     newParagraph.InsertAfterSelf(table); 
    } 

    // close saves all parts and closes the document 
    document.Close(); 
} 

上面的代碼應該這樣做。不過,我會解釋一些特殊情況。

注意,它將書籤的父元素之後嘗試插入。如果您的書籤恰好是表格中段落的一部分,您會期望什麼行爲?是否應該在該表之後附加新的段落和表格?還是應該在桌子後面做?

您可能想知道爲什麼上述問題很重要。這完全取決於插入的位置。如果書籤的父節點在表中,那麼當前上面的代碼將試圖在表中放置一個表。這很好,但是由於OpenXml結構無效,可能會發生錯誤。原因是如果插入的表是原始表的TableCell中的最後一個元素,則需要在關閉TableCell標記之後添加一個Paragraph元素。如果嘗試在MS Word中打開文檔時發生此問題,您會立即發現此問題。

的解決方案是確定你是否真的在表內進行插入。

要做到這一點,我們可以添加到上面的代碼(父VAR後):

var parent = bookmark.Parent; // bookmark's parent element 

    // loop till we get the containing element in case bookmark is inside a table etc. 
    // keep checking the element's parent and update it till we reach the Body 
    var tempParent = bookmark.Parent; 
    bool isInTable = false; 
    while (tempParent.Parent != mainPart.Document.Body) 
    { 
     tempParent = tempParent.Parent; 
     if (tempParent is Table && !isInTable) 
      isInTable = true; 
    } 

    // ... 

    newParagraph.InsertAfterSelf(table); // from above sample 
    // if bookmark is in a table, add a paragraph after table 
    if (isInTable) 
     table.InsertAfterSelf(new Paragraph()); 

這應該防止出現錯誤,並給你有效的OpenXML的。如果你對我之前的問題回答「是」,並且希望在父表之後執行插入操作,而不是像上面的代碼那樣在表內執行插入操作,則可以使用while循環構思。如果是這樣的情況下,上述問題將不再是一個問題,你可以替換回路上與以下布爾:

var parent = bookmark.Parent; // bookmark's parent element 
    while (parent.Parent != mainPart.Document.Body) 
    { 
     parent = parent.Parent; 
    } 

這使得重新分配的父母,直到它在身體層面主要包含元素。因此,如果書籤位於表格中的段落中,它將從段落到TableCell到TableRow到表格並停止,因爲表格的父元素是主體。在這一點上,parent = Table元素,我們可以在它之後插入。

這應該涉及到一些不同的方法,這取決於你的原意。試用後,如果您需要澄清,請告訴我。

文檔反射

你也許想知道我是怎麼確定的GridColumn.Width值。我製作了一張桌子,並使用了文檔反射器工具來獲取它。當您安裝Open Xml SDK時,生產力工具(如果您安裝了它們)將位於C:\Program Files\Open XML Format SDK\V2.0\tools(或類似)中。

瞭解*的最佳方法。docx格式工作(或任何Open Xml格式的文檔)是使用文檔反射器工具打開現有文件。瀏覽文檔部分,然後找到要複製的項目。該工具向您顯示用於生成整個文檔的實際代碼。這是您可以複製/粘貼到您的應用程序以生成類似結果的代碼。您可以通常忽略所有參考ID;你必須看一看,並試着去感受它。

正如我所提到的,上面的表代碼是根據示例文檔改編的。我在docx中添加了一個簡單的表格,然後在工具中打開它,並複製該工具生成的代碼(我刪除了一些額外的代碼來清理它)。這給了我一個工作示例來添加一個表。

,當你想知道如何編寫代碼,生成一些東西,比如格式化的表格和段落樣式等

看看包含此鏈接的其他工具的截圖和信息,是特別有幫助在SDK中:An introduction to Open XML SDK 2.0

代碼段

您可能也有興趣的代碼片段的Open XML。有關片段列表,請檢查this blog post。你可以從這裏下載它們:2007 Office System Sample: Open XML Format SDK 2.0 Code Snippets for Visual Studio 2008

安裝完成後,您可以從Tools |代碼片段管理器菜單。選擇C#作爲語言,點擊添加按鈕,然後導航到PersonalFolder \ Visual Studio 2008 \ Code Snippets \ Visual C#\ Open Office SDK 2.0 for Microsoft Office以添加它們。從你的代碼中,你可以右鍵單擊並選擇「Insert Snippet」並選擇你想要的。

+0

按設計(或定義),段落總是以新行開始。這聽起來像是你想將新文本附加到書籤的父段,所以文本繼續在同一行,是嗎?段落可以包含多個「運行」,其中又包含「文本」項目。要附加到段落,您需要爲其添加新的運行。如果這就是你想要的,創建Run var並添加它的Text後,不要將它添加到** newParagraph **。相反,將它添加到父:** parent.Append(run); **然後添加表後:** parent.InsertAfterSelf(表); ** - 看看是否它:) – 2009-10-27 13:49:50