Find and Replace String with Plain Textbox

Using a previous post, I was able to use the find and replace method to find all strings “X” and change them to a checkbox. When trying to do similar with changing all “___” to plain textboxes, I keep getting an error that the parent node is null.
FindAndReplace_AsposeWords.zip (13.6 KB)

@amber.louk In your case you can use one replace operation to achieve this. For example please try using the following code:

Document doc = new Document(@"C:\Temp\in.docx");
FindReplaceOptions opt = new FindReplaceOptions();
opt.ReplacingCallback = new ReplaceWithSdtCallback();
opt.Direction = FindReplaceDirection.Backward;
doc.Range.Replace(new Regex("(___)|(_X_)"), "", opt);
doc.Save(@"C:\Temp\out.docx");
internal class ReplaceWithSdtCallback : IReplacingCallback
{
    /// <summary>
    /// This method is called by the Aspose.Words find and replace engine for each match.
    /// </summary>
    ReplaceAction IReplacingCallback.Replacing(ReplacingArgs e)
    {
        Document doc = (Document)e.MatchNode.Document;

        // 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 deleting.
        List<Run> runs = new List<Run>();

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

        // Create DocumentBuilder to insert HTML.
        DocumentBuilder builder = new DocumentBuilder(doc);
        // Move builder to the first run.
        builder.MoveTo(runs[0]);

        Node replacementNode = null;
        if (e.Match.Value == "_X_")
        {
            StructuredDocumentTag sdtCheckBox = new StructuredDocumentTag(builder.Document, SdtType.Checkbox, MarkupLevel.Inline);
            sdtCheckBox.Title = string.Format("checlbox_{0}", mCounter++);
            sdtCheckBox.Checked = false;
            sdtCheckBox.ContentsFont.Size = builder.Font.Size;
            replacementNode = sdtCheckBox;
        }
        else if (e.Match.Value == "___")
        {
            StructuredDocumentTag plnTextBox = new StructuredDocumentTag(builder.Document, SdtType.PlainText, MarkupLevel.Inline);
            plnTextBox.Title = string.Format("text_{0}", mCounter++);
            replacementNode = plnTextBox;
        }

        // Insert a checkbox form field.
        builder.InsertNode(replacementNode);

        // Delete matched runs
        foreach (Run run in runs)
            run.Remove();

        // Signal to the replace engine to do nothing because we have already done all what we wanted.
        return ReplaceAction.Skip;
    }

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

    private int mCounter = 0;
}

I tried this fix, and it gets rid of the error but there are no form fields populating on the document when I run it.

@amber.louk The code works properly on my side and placeholders are replaced with corresponding SDTs. Here is output produced on my side using the above code and the latest 23.10 version of Aspose.Words: out.docx (14.7 KB)

I’m unfamiliar with this line of code, specifically how to implement the “ReplaceWithSdtCallback()” class. Could you provide the class to help me set it up?

@amber.louk The implementation is provided in my previous answer:
https://forum.aspose.com/t/find-and-replace-string-with-plain-textbox/273253/2

I named my class something different and that’s why it wasn’t working. I fixed that issue and it worked perfectly, thank you!!!

1 Like