Write to Content Control after specified body text

Thank you, however this still does not work.

Firstly I had to change the sdt Tag as it was hardcoded (an easy fix), but more importantly, when calling it using the following code, it writes to 2 content controls below 2 different bookmarks, not just the one specified:

WriteFieldAfter(@“c:\temp\sample doc.docx”,
@“c:\temp\sample output.docx”,
“KEEPERS”,
“resident vet”,
“Name”,
“SUCCESS”);

private static void WriteFieldAfter(string filenameAndPath, string bookmarkName, string searchText, string contentControlTag, string insertionText)
{

        Document doc = new Document(filenameAndPath);
        FindReplaceOptions options = new FindReplaceOptions();
        FindAndReplace findAndReplace = new FindAndReplace
        {
            BookmarkName = bookmarkName,
            InsertionText = insertionText,
            ContentControlTag = contentControlTag
        };

        options.ReplacingCallback = findAndReplace;
        doc.Range.Replace(new Regex(searchText), searchText, options);
        doc.Save(filenameAndPath);

    }


    private class FindAndReplace : IReplacingCallback
    {
        public object BookmarkName { get; internal set; }
        public string InsertionText { get; internal set; }
        public string ContentControlTag { get; internal set; }

        ReplaceAction IReplacingCallback.Replacing(ReplacingArgs e)
        {

            // This is a Run node that contains either the beginning or the complete match.
            Node currentNode = e.MatchNode;

            // The first (and may be the only) run can contain text before the match,
            // in this case it is necessary to split the run.
            if (e.MatchOffset > 0)
                currentNode = SplitRun((Run)currentNode, e.MatchOffset);

            // This array is used to store all nodes of the match for further removing.
            ArrayList runs = new ArrayList();

            // Find all runs that contain parts of the match string.
            int remainingLength = e.Match.Value.Length;

            while ((remainingLength > 0) &&
                (currentNode != null) &&
                (currentNode.GetText().Length <= remainingLength))
            {
                runs.Add(currentNode);
                remainingLength = remainingLength - currentNode.GetText().Length;

                // Select the next Run node.
                // Have to loop because there could be other nodes such as BookmarkStart etc.
                do
                {
                    currentNode = currentNode.NextSibling;
                }
                while ((currentNode != null) && (currentNode.NodeType != NodeType.Run));
            }

            // Split the last run that contains the match if there is any text left.
            if ((currentNode != null) && (remainingLength > 0))
            {
                SplitRun((Run)currentNode, remainingLength);
                runs.Add(currentNode);
            }

            // To do
            Document doc = ((Document)e.MatchNode.Document);
            Bookmark bm = doc.Range.Bookmarks[BookmarkName.ToString()];
            Paragraph bmPara = (Paragraph)bm.BookmarkStart.GetAncestor(NodeType.Paragraph);

            if (bmPara != null)
            {
                int bmParaIndex = doc.GetChildNodes(NodeType.Paragraph, true).IndexOf(bmPara);
                Paragraph start = ((Run)runs[0]).ParentParagraph;
                int searchTextParaIndex = doc.GetChildNodes(NodeType.Paragraph, true).IndexOf(start);

                if (searchTextParaIndex > bmParaIndex)
                {
                    StructuredDocumentTag targetSdt = null;
                    Paragraph para = start;
                    bool flag = true;
                    while (para != null && flag)
                    {
                        foreach (StructuredDocumentTag sdt in para.GetChildNodes(NodeType.StructuredDocumentTag, true))
                        {
                            if (sdt.Tag.Equals(ContentControlTag))
                            {
                                targetSdt = sdt;
                                flag = false;
                                break;
                            }
                        }
                        para = (Paragraph)para.NextSibling;
                    }

                    if (targetSdt != null)
                    {
                        targetSdt.IsShowingPlaceholderText = false;
                        Run newTextRun = new Run(start.Document, InsertionText);
                        newTextRun.Font.Name = "Arial";
                        newTextRun.Font.Size = 10;
                        targetSdt.RemoveAllChildren();

                        if (targetSdt.Level == MarkupLevel.Inline)
                        {
                            targetSdt.AppendChild(newTextRun);
                        }

                        if (targetSdt.Level == MarkupLevel.Block)
                        {
                            para.ParagraphFormat.Alignment = ParagraphAlignment.Left;
                            para.AppendChild(newTextRun);
                            targetSdt.AppendChild(para);
                        }
                    }
                }
            }
            return ReplaceAction.Skip;
        }

        private static Run SplitRun(Run run, int position)
        {
            Run afterRun = (Run)run.Clone(true);
            afterRun.Text = run.Text.Substring(position);
            run.Text = run.Text.Substring((0), (0) + (position));
            run.ParentNode.InsertAfter(afterRun, run);
            return afterRun;
        }
    }

@SCDGLC,

The following code should fix the problem with this case as well:

WriteFieldAfter("E:\\temp\\sample\\Sample Doc.docx",
                "E:\\Temp\\sample\\19.9.docx",
                "KEEPERS",
                "resident vet",
                "Name",
                "success"); 

private static void WriteFieldAfter(string filenameAndPath, string outputfilenameAndPath, string bookmarkName, string searchText, string contentControlTag, string insertionText)
{
    Document doc = new Document(filenameAndPath);
    FindReplaceOptions options = new FindReplaceOptions();
    FindAndReplace findAndReplace = new FindAndReplace
    {
        BookmarkName = bookmarkName,
        InsertionText = insertionText,
        ContentControlTag = contentControlTag
    };

    options.ReplacingCallback = findAndReplace;
    doc.Range.Replace(new Regex(searchText), searchText, options);
    doc.Save(outputfilenameAndPath);
}

private class FindAndReplace : IReplacingCallback
{
    public object BookmarkName { get; internal set; }
    public string InsertionText { get; internal set; }
    public string ContentControlTag { get; internal set; }

    ReplaceAction IReplacingCallback.Replacing(ReplacingArgs e)
    {
        // This is a Run node that contains either the beginning or the complete match.
        Node currentNode = e.MatchNode;

        // The first (and may be the only) run can contain text before the match,
        // in this case it is necessary to split the run.
        if (e.MatchOffset > 0)
            currentNode = SplitRun((Run)currentNode, e.MatchOffset);

        // This array is used to store all nodes of the match for further removing.
        ArrayList runs = new ArrayList();

        // Find all runs that contain parts of the match string.
        int remainingLength = e.Match.Value.Length;

        while ((remainingLength > 0) &&
            (currentNode != null) &&
            (currentNode.GetText().Length <= remainingLength))
        {
            runs.Add(currentNode);
            remainingLength = remainingLength - currentNode.GetText().Length;

            // Select the next Run node.
            // Have to loop because there could be other nodes such as BookmarkStart etc.
            do
            {
                currentNode = currentNode.NextSibling;
            }
            while ((currentNode != null) && (currentNode.NodeType != NodeType.Run));
        }

        // Split the last run that contains the match if there is any text left.
        if ((currentNode != null) && (remainingLength > 0))
        {
            SplitRun((Run)currentNode, remainingLength);
            runs.Add(currentNode);
        }

        // To do
        Document doc = ((Document)e.MatchNode.Document);
        Bookmark bm = doc.Range.Bookmarks[BookmarkName.ToString()];
        Paragraph bmPara = (Paragraph)bm.BookmarkStart.GetAncestor(NodeType.Paragraph);

        if (bmPara != null)
        {
            int bmParaIndex = doc.GetChildNodes(NodeType.Paragraph, true).IndexOf(bmPara);
            Paragraph start = ((Run)runs[0]).ParentParagraph;
            int searchTextParaIndex = doc.GetChildNodes(NodeType.Paragraph, true).IndexOf(start);

            if (searchTextParaIndex > bmParaIndex)
            {
                StructuredDocumentTag targetSdt = null;
                Paragraph para = start;
                bool flag = true;
                while (para != null && flag)
                {
                    foreach (StructuredDocumentTag sdt in para.GetChildNodes(NodeType.StructuredDocumentTag, true))
                    {
                        if (sdt.Tag.Equals(ContentControlTag))
                        {
                            targetSdt = sdt;
                            flag = false;
                            break;
                        }
                    }
                    para = (Paragraph)para.NextSibling;
                }

                if (targetSdt != null)
                {
                    targetSdt.IsShowingPlaceholderText = false;
                    Run newTextRun = new Run(start.Document, InsertionText);
                    newTextRun.Font.Name = "Arial";
                    newTextRun.Font.Size = 10;
                    targetSdt.RemoveAllChildren();

                    if (targetSdt.Level == MarkupLevel.Inline)
                    {
                        targetSdt.AppendChild(newTextRun);
                    }

                    if (targetSdt.Level == MarkupLevel.Block)
                    {
                        para.ParagraphFormat.Alignment = ParagraphAlignment.Left;
                        para.AppendChild(newTextRun);
                        targetSdt.AppendChild(para);
                    }

                    return ReplaceAction.Stop;
                }
            }
        }
        return ReplaceAction.Skip;
    }

    private static Run SplitRun(Run run, int position)
    {
        Run afterRun = (Run)run.Clone(true);
        afterRun.Text = run.Text.Substring(position);
        run.Text = run.Text.Substring((0), (0) + (position));
        run.ParentNode.InsertAfter(afterRun, run);
        return afterRun;
    }
}

It took a while but I think we’re there - thank you!

Sorry one more question (hopefully!), how would I do exactly the same thing but without having search text? In other words, I want to populate the first empty content control that comes after a specified bookmark but I won’t be using any search text this time so just the first empty content control with the specified tag that is found under the specified bookmark name.

I was hoping that I could simply set a bookmark and builder objects and move to the bookmark start and then loop round any child nodes of the bookmark and read their content control tags, but it seems that this is not possible.

Here is the sample document and expected output. Note that as before I want to have the bookmark name, filename, content control tag and insertion text to all be generic but this time I don’t want to include any search text. Many thanks…

Sample Doc2.zip (57.0 KB)

@SCDGLC,

You can build logic on the following code to get the desired output:

Document doc = new Document("E:\\Temp\\Sample Doc2\\Sample Doc.docx");

Bookmark bm = doc.Range.Bookmarks["SPECIALISTS"];

Paragraph start = (Paragraph)bm.BookmarkStart.GetAncestor(NodeType.Paragraph);
StructuredDocumentTag targetSdt = null;

Paragraph para = (Paragraph)start;
bool flag = true;
while (para != null && flag)
{
    foreach (StructuredDocumentTag sdt in para.GetChildNodes(NodeType.StructuredDocumentTag, true))
    {
        if (sdt.Tag.Equals("Name") && sdt.ToString(SaveFormat.Text).Trim().Equals("Click or tap here to enter text."))
        {
            targetSdt = sdt;
            flag = false;
            break;
        }
    }

    para = (Paragraph)para.NextSibling;
}

if (targetSdt != null)
{
    targetSdt.IsShowingPlaceholderText = false;
    Run newTextRun = new Run(start.Document, "SUCCESS");
    newTextRun.Font.Name = "Arial";
    newTextRun.Font.Size = 10;
    targetSdt.RemoveAllChildren();

    targetSdt.AppendChild(newTextRun);
}

doc.Save("E:\\Temp\\Sample Doc2\\19.9.docx");

Fantastic - many thanks! :slight_smile:

Oops, spoke to soon. This generates the following error, presumably when encountering a table:

Unable to cast object of type ‘Aspose.Words.Tables.Table’ to type ‘Aspose.Words.Paragraph’.

I’m just trawling through the test data to see if I can spot what caused it as I was running multiple files when it crashed and I can’t step through as I’m running through 3rd party software. Will let you know if I spot the culprit.

    private static void WriteSingleFieldValue_NoSearch(string filenameAndPath, string bookmarkName, string contentControlTag, string insertionText)
    {

        Document doc = new Document(filenameAndPath);
        Bookmark bm = doc.Range.Bookmarks[bookmarkName];
        Paragraph start = (Paragraph)bm.BookmarkStart.GetAncestor(NodeType.Paragraph);
        StructuredDocumentTag targetSdt = null;
        Paragraph para = (Paragraph)start;
        bool flag = true;

        while (para != null && flag)
        {
            foreach (StructuredDocumentTag sdt in para.GetChildNodes(NodeType.StructuredDocumentTag, true))
            {
                if ((sdt.Tag.Equals(contentControlTag))
                    && ((sdt.ToString(SaveFormat.Text).Trim().Equals("Click or tap here to enter text."))
                    || (sdt.ToString(SaveFormat.Text).Trim().Equals(""))
                    || (sdt.ToString(SaveFormat.Text).Trim().Equals(contentControlTag))
                    || sdt.ToString(SaveFormat.Text).Trim().Equals(contentControlTag + "_" + contentControlTag)))
                {
                    targetSdt = sdt;
                    flag = false;
                    break;
                }
            }
            para = (Paragraph)para.NextSibling;
        }

        if (targetSdt != null)
        {
            targetSdt.IsShowingPlaceholderText = false;
            Run newTextRun = new Run(start.Document, insertionText);
            newTextRun.Font.Name = "Arial";
            newTextRun.Font.Size = 10;
            targetSdt.RemoveAllChildren();
            targetSdt.AppendChild(newTextRun);
        }

        doc.Save(filenameAndPath);

    }

This is the file that it must have fallen over on when trying to write to the content controls using the code above…

Sample With Table.zip (21.7 KB)

@SCDGLC,

I am afraid, we do not see any Bookmarks in your newly shared ‘Sample With Table.docx’ document. Can you please also share your expected document showing the desired output? Also, how are you calling the ‘WriteSingleFieldValue_NoSearch’ method i.e. what are the parameter values you are supplying to this method?

Here is a sample file with bookmarks and an expected output file.

Sample With Table.zip (50.6 KB)

And this is my code:

WriteSingleFieldValue_NoSearch(@“c:\temp\Sample With Table.docx”,@“c:\temp\Sample With Table Output.docx”, “LimitOfLiability”, “contract_fields_claimsprep_value”, “99999”);

    private static void WriteSingleFieldValue_NoSearch(string filenameAndPath, string outputFilenameAndPath, string bookmarkName, string contentControlTag, string insertionText)
    {

        Document doc = new Document(filenameAndPath);
        Bookmark bm = doc.Range.Bookmarks[bookmarkName];
        Paragraph start = (Paragraph)bm.BookmarkStart.GetAncestor(NodeType.Paragraph);
        StructuredDocumentTag targetSdt = null;
        Paragraph para = (Paragraph)start;
        bool flag = true;

        while (para != null && flag)
        {
            foreach (StructuredDocumentTag sdt in para.GetChildNodes(NodeType.StructuredDocumentTag, true))
            {
                if ((sdt.Tag.Equals(contentControlTag))
                    && ((sdt.ToString(SaveFormat.Text).Trim().Equals("Click or tap here to enter text."))
                    || (sdt.ToString(SaveFormat.Text).Trim().Equals(""))
                    || (sdt.ToString(SaveFormat.Text).Trim().Equals(contentControlTag))
                    || sdt.ToString(SaveFormat.Text).Trim().Equals(contentControlTag + "_" + contentControlTag)))
                {
                    targetSdt = sdt;
                    flag = false;
                    break;
                }
            }
            para = (Paragraph)para.NextSibling;
        }

        if (targetSdt != null)
        {
            targetSdt.IsShowingPlaceholderText = false;
            Run newTextRun = new Run(start.Document, insertionText);
            newTextRun.Font.Name = "Arial";
            newTextRun.Font.Size = 10;
            targetSdt.RemoveAllChildren();
            targetSdt.AppendChild(newTextRun);
        }

        doc.Save(outputFilenameAndPath);

    }

@SCDGLC,

Please try using the following code:

private static void WriteSingleFieldValue_NoSearch(string filenameAndPath, string outputFilenameAndPath, string bookmarkName, string contentControlTag, string insertionText)
{
    Document doc = new Document(filenameAndPath);
    Bookmark bm = doc.Range.Bookmarks[bookmarkName];
    Paragraph start = (Paragraph)bm.BookmarkStart.GetAncestor(NodeType.Paragraph);
    StructuredDocumentTag targetSdt = null;
    CompositeNode node = (CompositeNode)start;
    bool flag = true;

    while (node != null && flag)
    {
        foreach (StructuredDocumentTag sdt in node.GetChildNodes(NodeType.StructuredDocumentTag, true))
        {
            if ((sdt.Tag.Equals(contentControlTag))
                && ((sdt.ToString(SaveFormat.Text).Trim().Equals("Click or tap here to enter text."))
                || (sdt.ToString(SaveFormat.Text).Trim().Equals(""))
                || (sdt.ToString(SaveFormat.Text).Trim().Equals(contentControlTag))
                || sdt.ToString(SaveFormat.Text).Trim().Equals(contentControlTag + "_" + contentControlTag)))
            {
                targetSdt = sdt;
                flag = false;
                break;
            }
        }
        node = (CompositeNode) node.NextSibling;
    }

    if (targetSdt != null)
    {
        targetSdt.IsShowingPlaceholderText = false;
        Run newTextRun = new Run(start.Document, insertionText);
        newTextRun.Font.Name = "Arial";
        newTextRun.Font.Size = 10;
        targetSdt.RemoveAllChildren();
        targetSdt.AppendChild(newTextRun);
    }

    doc.Save(outputFilenameAndPath);
}

Hope, this helps.

That looks like it should work - many thanks!!