7

この問題で頭がおかしくなりそうです。とても単純で見逃しているだけだと思いますが、C#のOpenXml SDK v2.0を使用してWord 2007のコンテンツコントロールのコンテンツを変更する方法を見つけることはできません。

プレーン テキスト コンテンツ コントロールを含む 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();
}

これによりテキストは変更されますが、すべての書式設定が失われます。誰も私がこれを行う方法を知っていますか?

4

6 に答える 6

7

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も参考になります。これは、リッチ テキスト コンテンツ コントロールに配置されたテーブルに行を追加する方法について説明しています。

于 2010-01-18T17:50:05.590 に答える
3

目的の結果を達成するための優れた方法の1つは、Open XMLSDK2.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

于 2010-01-22T01:53:46.137 に答える
3

sdtRunを削除して新しいものを追加する最初のアプローチでは、Runのみを追加し、RunStyleは追加しないため、明らかにフォーマットが削除されます。フォーマットを保持するには、次のような実行要素を作成する必要があります

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

すべてを置き換える2番目のアプローチはDecendants<Text>、リッチテキストコンテンツコントロールにSdtRun要素がないため、プレーンテキストコンテンツコントロールでのみ機能します。リッチテキストコンテンツコントロールは、SdtContent要素を持つSdtBlockです。リッチテキストコンテンツコントロールには、複数の段落、複数の実行、および複数のテキストを含めることができます。したがってsdtRun.Descendants<Text>().First().Text = replacementText、リッチテキストコンテンツコントロールのコードには欠陥があります。リッチコンテンツコントロールのテキスト全体を置き換えながら、すべてのフォーマットを保持する1行のコードはありません。

「最終的なドキュメントのコンテンツコントロールが削除されない」とはどういう意味かわかりませんでしたか?ここでの要件は、コンテンツコントロールとフォーマットを保持するだけでテキスト(コンテンツ)を変更することだと思いました。

于 2010-01-20T18:31:21.107 に答える
1

また、フッター内のテキストを見つけて置き換える必要がありました。次のコードを使用してそれらを見つけることができます。

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();
}
于 2010-01-20T19:46:56.487 に答える
1

別の解決策は

        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);
于 2011-03-06T12:41:42.340 に答える
1

コンテンツ コントロール タイプ

Word 文書の挿入ポイントに応じて、次の 2 種類のコンテンツ コントロールが作成されます。

  • トップレベル (段落と同じレベル)

  • ネスト (通常は既存の段落内)

紛らわしいことに、XML では両方の型が としてタグ付けされて<sdt>...</sdt>いますが、基礎となる openXML クラスは異なります。トップレベルの場合、ルートはSdtBlockで、コンテンツはSdtContentBlockです。入れ子の場合はSdtRun&SdtContentRunです。

両方のタイプ、つまりすべてのコンテンツ コントロールを取得するには、共通の基本クラスを介して反復しSdtElement、タイプを確認することをお勧めします。

List<SdtElement> sdtList = document.Descendants<SdtElement>().ToList();

foreach( SdtElement sdt in sdtList )
{
   if( sdt is SdtRun )
   {
      ; // process nested sdts
   }

   if( sdt is SdtBlock )
   {
      ; // process top-level sdts
   }
}

ドキュメント テンプレートの場合、すべてのコンテンツ コントロールを処理する必要があります。複数のコンテンツ コントロールが同じタグ名 (例: 顧客名) を持つことはよくあることであり、通常、これらすべてを実際の顧客名に置き換える必要があります。

コンテンツ コントロール タグ名

content-control tag-name は決して分割されません。

XML では、これは次のとおりです。

<w:sdt>
...
<w:sdtPr>
...
<w:tag w:val="customer-name"/>

タグ名は決して分割されないため、常に直接一致して見つけることができます。

   List<SdtElement> sdtList = document.Descendants<SdtElement>().ToList();
        
   foreach( SdtElement sdt in sdtList )
   {
       if( sdt is SdtRun )
       {
         String tagName = sdt.SdtProperties.GetFirstChild<Tag>().Val;

         if( tagName == "customer-name" )
         {
            ; // get & replace placeholder with actual value
         }

明らかに、上記のコードでは、それぞれの異なるタグ名に対応する実際の値を取得するためのより洗練されたメカニズムが必要になります。

コンテンツ コントロール テキスト

コンテンツ コントロール内では、レンダリングされたテキストが複数のランに分割されることは非常に一般的です (各ランが同じプロパティを持っているにもかかわらず)。

とりわけ、これはスペル/文法チェッカーと編集試行回数によって引き起こされます。[customer-name] などの区切り文字が使用されている場合、テキスト分割はより一般的です。

これが重要な理由は、XML をチェックしないと、プレースホルダー テキストが分割されていないことを保証できないため、見つけて置き換えることができないからです。

推奨されるアプローチの 1 つ

推奨されるアプローチの 1 つは、最上位および/またはネストされたプレーンテキストのコンテンツ コントロールのみを使用することです。

  • タグ名で content-control を見つける

  • 書式設定された段落を挿入するか、コンテンツ コントロールの後に実行します

  • コンテンツ コントロールを削除する

     List<SdtElement> sdtList = document.Descendants<SdtElement>().ToList();
    
     foreach( SdtElement sdt in sdtList )
     {
        if( sdt is SdtRun )
        {
           String tagName = sdt.SdtProperties.GetFirstChild<Tag>().Val;
    
           String newText = "new text"; // eg GetTextByTag( tagName );
    
           // should use a style or common run props
    
           RunProperties runProps = new RunProperties();
    
           runProps.Color    = new Color   () { Val   = "000000" };
           runProps.FontSize = new FontSize() { Val   = "23" };
           runProps.RunFonts = new RunFonts() { Ascii = "Calibri" };
    
           Run run = new Run();
    
           run.Append( runProps );
           run.Append( new Text( newText ) );
    
           sdt.InsertAfterSelf( run );
    
           sdt.Remove();
        }
    
        if( sdt is SdtBlock )
        {
           ; // add paragraph
        }
     }
    

トップレベルの型の場合、段落を挿入する必要があります。

このアプローチでは、コンテンツ コントロールは、(タグ名によって) 確実に見つかるプレースホルダーとしてのみ使用され、適切なテキスト (一貫してフォーマットされている) に完全に置き換えられます。

また、これにより、コンテンツ コントロール テキストをフォーマットする必要がなくなります (分割されて見つからない可能性があります)。

タグ名に適切な命名規則 (Xpath 式など) を使用すると、他の XML ドキュメントを使用してテンプレートに入力するなど、さらに可能性が広がります。

于 2021-02-12T18:45:08.160 に答える