Pregunta

I'm an experienced C# developer who drew the short straw on this particular phase of the project and got to work on document generation (no-one on the team has any particular experience with this).

For Word output, so far, I've been using Content Controls embedded in documents and some of the methods in the DocumentFormat.OpenXml package to create fields I can identify in code and dynamically replace with the appropriate data. So I load a template, and loop through the content controls like this:

string template = serverRoot + @"Templates\MyTemplate.docx";
string path = serverRoot + @"DataOut\\" + clientName + "\\MyDocument.docx";
File.Copy(template, path, true);

using (WordprocessingDocument newDoc = WordprocessingDocument.Open(path, true))
{
    MainDocumentPart mainPart = newDoc.MainDocumentPart;
    var placeHolders = mainPart.Document.Body.Descendants<SdtElement>();

    foreach (var sdtRun in placeHolders)
    {
        Console.WriteLine("Found Field: " + sdtRun.SdtProperties.GetFirstChild<Tag>().Val.Value);
        switch (sdtRun.SdtProperties.GetFirstChild<Tag>().Val.Value)
        {
            //check the content control values and replace them
        }
    }
}

However, my next document requires an indeterminate number of repeat elements depending on the situation. Or to put it another way, for each file in the package it need to output something like this into the doc:

Filename: <insert filename>
Quantity: <insert quantity>
Date:     <insert date>

But we don't know in advance how many files there will be.

AFAIK I can't do this with a standard content control, at least not while retaining any kind of sensible formatting.

What's the best approach here. Should I generate the whole text as a block, adding in line breaks and such, and stuff it all into one Content Control? Create new Content Controls on the fly (and if so, how)? Or is there a better way?

¿Fue útil?

Solución

I took the coward's way out of this one in the end, and used one content control to which I added a detailed Run object built up inside a loop:

private Run ParseForOpenXML(string textualData)
{
    Run run = new Run();

    //split string on paragraph breaks, and create a Break object for each
    string[] newLineArray = { Environment.NewLine, "\n" };
    string[] textArray = textualData.Split(newLineArray, StringSplitOptions.None);
    bool first = true;

    foreach (string line in textArray)
    {
        if (!first)
        {
            run.Append(new Break());
        }
        first = false;

        //split string on tab breaks, and create a new TabChar object for each
        bool tFirst = true;
        string[] tabArray = line.Split('\t');
        foreach(string fragment in tabArray)
        {
            if (!tFirst)
            {
                run.Append(new TabChar());
            }
            tFirst = false;

            Text txt = new Text();
            txt.Text = fragment;
            run.Append(txt);
        }
    }

    return run;
}

Otros consejos

Recent versions of Word do support a repeat content control; see https://stackoverflow.com/a/20676863/1031689

Alternatively, you can create your own "repeat" control. My OpenDoPE convention is one way of doing this. You can use these in Word 2007 and later. You can process them using docx4j (Java), or, docx4j.NET. To set up a docx with suitable content controls, you'll need the authoring addin.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top