2009-10-23 9 views
4

j'ai pu accéder à un signet dans mon document Word en utilisant ce code:Insérer OpenXmlElement après mot signet dans Open XML SDK

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

Maintenant, je veux insérer un paragraphe et une table après ce signet. Comment je fais ça? (Exemple de code serait apprécié)

Répondre

24

code

Une fois que vous avez le signet, vous pouvez accéder à son élément parent et ajouter les autres éléments après.

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(); 
} 

Le code ci-dessus devrait le faire. Cependant, je vais expliquer certaines circonstances particulières. Sachez qu'il tentera les insertions après l'élément parent du signet. Quel comportement attendez-vous si votre marque-page fait partie d'un paragraphe à l'intérieur d'une table? Devrait-il ajouter le nouveau paragraphe et le nouveau tableau juste après, dans ce tableau? Ou devrait-il le faire après cette table?

Vous vous demandez peut-être pourquoi les questions ci-dessus sont importantes. Tout dépend de l'endroit où l'insertion se produira. Si le parent du signet est dans une table, le code ci-dessus tente actuellement de placer une table dans une table. C'est bien, cependant une erreur peut se produire en raison d'une structure OpenXml non valide. La raison en est que si la table insérée était le dernier élément de TableCell de la table d'origine, un élément Paragraphe doit être ajouté après la balise TableCell de fermeture. Vous découvrirez rapidement ce problème s'il s'est produit une fois que vous avez essayé d'ouvrir le document dans MS Word.

La solution consiste à déterminer si vous effectuez effectivement l'insertion dans une table.

Pour ce faire, nous pouvons ajouter au code ci-dessus (après le var parent):

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()); 

Cela devrait éviter l'erreur de se produire et vous donner OpenXml valide. L'idée de boucle while peut être utilisée si vous avez répondu "yes" à ma question précédente et que vous souhaitez effectuer l'insertion après la table parent plutôt qu'à l'intérieur de la table comme le ferait le code ci-dessus. Si tel est le cas, la question ci-dessus ne serait plus une préoccupation et vous pouvez remplacer cette boucle et booléens avec les éléments suivants:

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

Cela permet de maintenir réattribuant le parent jusqu'à ce qu'il soit le principal élément contenant au niveau du corps . Donc, si le signet se trouvait dans un paragraphe qui se trouvait dans une table, il irait de Paragraph à TableCell à TableRow à Table et s'arrêterait là puisque le parent de la Table est le Corps. À ce stade parent = Table élément et nous pouvons insérer après.

Cela devrait couvrir différentes approches, en fonction de votre intention d'origine. Faites-moi savoir si vous avez besoin d'éclaircissements après l'avoir essayé.

document réflecteur

Vous pourriez vous demander comment je déterminé les valeurs GridColumn.Width. J'ai créé une table et utilisé l'outil Document Reflector pour l'obtenir. Lorsque vous avez installé le SDK Open Xml, les outils de productivité (si vous les avez installés) se trouvent au C:\Program Files\Open XML Format SDK\V2.0\tools (ou similaire).

La meilleure façon d'apprendre comment le *.Le format docx (ou tout document au format Open Xml) permet d'ouvrir un fichier existant avec l'outil Document Reflector. Parcourez la partie du document et localisez les éléments que vous souhaitez répliquer. L'outil vous montre le code réel utilisé pour générer le document entier. C'est un code que vous pouvez copier/coller dans votre application pour générer des résultats similaires. Vous pouvez ignorer tous les ID de référence en général. vous devrez jeter un coup d'oeil et l'essayer pour avoir une idée pour cela.

Comme je l'ai mentionné, le code de tableau ci-dessus a été adapté à partir d'un document d'exemple. J'ai ajouté une simple table à un docx, puis l'ai ouvert dans l'outil, et copié le code généré par l'outil (j'ai supprimé quelques extras pour le nettoyer). Cela m'a donné un échantillon de travail pour ajouter une table.

Il est particulièrement utile lorsque vous voulez savoir comment écrire du code qui génère quelque chose, comme les tables et les paragraphes formatés avec des styles etc.

Jetez un oeil à ce lien pour les captures d'écran et des informations sur les autres outils inclus dans le SDK: An introduction to Open XML SDK 2.0.

Code Snippets

Vous pourriez également être intéressé par les extraits de code pour Xml Ouvrir. Pour une liste d'extraits, vérifiez this blog post. Vous pouvez les télécharger ici: 2007 Office System Sample: Open XML Format SDK 2.0 Code Snippets for Visual Studio 2008.

Une fois installé, vous devez les ajouter à partir de Outils | Menu Gestionnaire de snippet de code. Sélectionnez C# pour la langue, cliquez sur le bouton Ajouter et accédez à PersonalFolder \ Visual Studio 2008 \ Extraits de code \ Visual C# \ Open XML SDK 2.0 pour Microsoft Office pour les ajouter. À partir de votre code, faites un clic droit et sélectionnez "Insérer un extrait" et sélectionnez celui que vous voulez.

+0

Par conception (ou définition), un paragraphe commence toujours sur une nouvelle ligne. Il semble que vous souhaitiez ajouter le nouveau texte au paragraphe parent du signet afin que le texte continue sur la même ligne, n'est-ce pas? Un paragraphe peut contenir plusieurs "Exécutions", qui contiennent à leur tour l'élément "Texte". Pour ajouter à un paragraphe, vous devez ajouter un nouveau Run. Si c'est ce que vous voulez, après avoir créé la variable Run et ajouté son texte, ne l'ajoutez pas à ** newParagraph **. Au lieu de cela, ajoutez-le au parent: ** parent.Append (run); ** Ensuite, ajoutez la table après: ** parent.InsertAfterSelf (table); ** - voir si c'est ça :) –

Questions connexes