Как изменить содержимое элемента управления содержимым в Word 2007 с помощью OpenXml SDK 2.0? - PullRequest
7 голосов
/ 16 января 2010

Схожу с ума от этой проблемы. Я уверен, что это так просто, я просто скучаю по нему, но я не могу понять, как изменить содержимое элемента управления содержимым в Word 2007 с помощью OpenXml SDK v2.0 в C #.

Я создал документ Word с контролем содержимого в виде простого текста. Тег для этого элемента управления - «FirstName». В коде я хотел бы открыть документ Word, найти этот элемент управления содержимым и изменить содержимое без потери форматирования.

Решение, которое я наконец-то нашел, заключалось в том, чтобы найти элемент управления контентом, вставить прогон после него, а затем удалить элемент управления контентом как таковой:

using (WordprocessingDocument wordProcessingDocument = WordprocessingDocument.Open(filePath, true)) {
MainDocumentPart mainDocumentPart = wordProcessingDocument.MainDocumentPart;
SdtRun sdtRun = mainDocumentPart.Document.Descendants<SdtRun>()
 .Where(run => run.SdtProperties.GetFirstChild<Tag>().Val == "FirstName").Single();

if (sdtRun != null) {
 sdtRun.Parent.InsertAfter(new Run(new Text("John")), sdtRun);
 sdtRun.Remove();
}

Это меняет текст, но я теряю все форматирование. Кто-нибудь знает, как я могу это сделать?

Ответы [ 5 ]

7 голосов
/ 18 января 2010

Я нашел лучший способ сделать это, используя http://wiki.threewill.com/display/enterprise/SharePoint+and+Open+XML#SharePointandOpenXML-UsingWord2007ContentControls в качестве ссылки. Ваши результаты могут отличаться, но я думаю, что это даст вам хорошее начало:

using (WordprocessingDocument wordprocessingDocument = WordprocessingDocument.Open(filePath, true)) {
    var sdtRuns = mainDocumentPart.Document.Descendants<SdtRun>()
        .Where(run => run.SdtProperties.GetFirstChild<Tag>().Val.Value == contentControlTagValue);

    foreach (SdtRun sdtRun in sdtRuns) {
        sdtRun.Descendants<Text>().First().Text = replacementText;
    }

    wordprocessingDocument.MainDocumentPart.Document.Save();
}

Я думаю, что выше будет работать только для элементов управления простым текстом. К сожалению, это не избавляет от контроля содержимого в итоговом документе. Если я получу эту работу, я опубликую ее.

http://msdn.microsoft.com/en-us/library/cc197932.aspx также является хорошим справочным материалом, если вы хотите найти элемент управления с расширенным текстом. В этом рассказывается о добавлении строк в таблицу, которая была помещена в элемент управления содержимым расширенного текста.

3 голосов
/ 22 января 2010

Один ОТЛИЧНЫЙ способ выяснить, как достичь желаемого результата, - использовать инструмент отражения документов, который поставляется с Open XML SDK 2.0 ....

Например, вы могли бы:

  1. В диалоговом окне "Свойства" для каждого элемента управления содержимым в вашем документе установите флажок "Удалить элемент управления содержимым при редактировании содержимого".
  2. Заполните их и сохраните как новый документ.
  3. Используйте отражатель для сравнения оригинальной и сохраненной версии.
  4. Нажмите кнопку показать / скрыть код, и он покажет вам код, необходимый для превращения оригинала в заполненную версию.

Это не идеально, но удивительно полезно. Вы также можете просто посмотреть на разметку любого документа и увидеть изменения, вызванные заполнением элементов управления.

Это довольно хрупкий способ сделать это, потому что Wordprocessing ML может быть сложным; это легко испортить. Для простых текстовых элементов управления я просто использую этот метод:

private void FillSimpleTextCC(SdtRun simpleTextCC, string replacementText)
    {
        // remove the showing place holder element      
        SdtProperties ccProperties = simpleTextCC.SdtProperties;
        ccProperties.RemoveAllChildren<ShowingPlaceholder>();

        // fetch content block Run element            
        SdtContentRun contentRun = simpleTextCC.SdtContentRun;
        var ccRun = contentRun.GetFirstChild<Run>();

        // if there was no placeholder text in the content control, then the SdtContentRun
        // block will be empty -> ccRun will be null, so create a new instance
        if (ccRun == null)
        {
            ccRun = new Run(
                new RunProperties() { RunStyle = null },
                new Text());
            contentRun.Append(ccRun);
        }

        // remove revision identifier & replace text
        ccRun.RsidRunProperties = null;
        ccRun.GetFirstChild<Text>().Text = replacementText;

        // set the run style to that stored in the SdtProperties block, if there was
        // one. Otherwise the existing style will be used.            
        var props = ccProperties.GetFirstChild<RunProperties>();
        if (props != null)
        if (props != null)
        {
            RunStyle runStyle = props.RunStyle;
            if (runStyle != null)
            {
                // set the run style to the same as content block property style.
                var runProps = ccRun.RunProperties;
                runProps.RunStyle = new RunStyle() { Val = runStyle.Val };
                runProps.RunFonts = null;
            }
        }
    }

Надеюсь, это поможет каким-то образом. : D

3 голосов
/ 20 января 2010

Ваш первый подход к удалению sdtRun и добавлению нового, очевидно, удалит форматирование, потому что вы добавляете только Run, но не RunStyle. Чтобы сохранить форматирование, вы должны создать элементы запуска, такие как

new Run( new RunProperties(new RunStyle(){ Val = "MyStyle" }),
                            new Text("Replacement Text"));

Ваш второй подход к замене всех Decendants<Text> будет работать для простого управления содержимым текста только потому, что у расширенного управления содержимым нет элемента SdtRun. Элемент управления Rich Text Content - это SdtBlock с элементами SdtContent. Элемент управления расширенным текстом может иметь несколько абзацев, несколько прогонов и несколько текстов. Таким образом, ваш код, sdtRun.Descendants<Text>().First().Text = replacementText, будет иметь недостатки для элемента управления Rich Text Content. Нет единого строчного кода, который мог бы заменить весь текст элемента управления расширенным содержимым и при этом сохранить все форматирование.

Я не понял, что вы подразумеваете под "не избавляется от контроля над контентом в итоговом документе"? Я думал, что ваше требование здесь состоит в том, чтобы изменить текст (контент) только путем сохранения управления контентом и форматирования.

1 голос
/ 06 марта 2011

Другое решение будет

        SdtRun rOld = p.Elements<SdtRun>().First();

        string OldNodeXML = rOld.OuterXml;
        string NewNodeXML = OldNodeXML.Replace("SearchString", "ReplacementString");

        SdtRun rNew = new SdtRun(NewNodeXML);


        p.ReplaceChild<SdtRun>(rNew, rOld);
1 голос
/ 20 января 2010

Мне также пришлось найти и заменить текст в нижних колонтитулах. Вы можете найти их, используя следующий код:

using (WordprocessingDocument wordprocessingDocument = WordprocessingDocument.Open(file.PhysicalFile.FullName, true)) {
    foreach (FooterPart footerPart in wordprocessingDocument.MainDocumentPart.FooterParts) {
        var footerPartSdtRuns = footerPart.Footer.Descendants<SdtRun>()
            .Where(run => run.SdtProperties.GetFirstChild<Tag>().Val.Value == contentControlTag);

        foreach (SdtRun sdtRun in footerPartSdtRuns) {
           sdtRun.Descendants<Text>().First().Text = replacementTerm;
        }
    }

    wordprocessingDocument.MainDocumentPart.Document.Save();
}
...