When writing a function to find and replace strings with textboxes of different sizes. We’ve been using the attached code and have found a way to add boxes, however they are not resizing as expected.
FindAndReplace.zip (11.6 KB)
@amber.louk Structured document tags textboxes does not have size, there are resized according to their inner content. By default they show text like "Click here to enter text"
. If you need to make the textbox wider or thinner, you should change this placeholder text accordingly.
If I’m unable to change the textbox size with the SDT without changing the inner content, what would you recommend using instead to still be able to find and replace but also change the sizes of textboxes?
@amber.louk Could you please create the expected output in MS Word and attach it here for our reference? We will check the document and provide you more information.
Generally, SDT and FormField textboxes are supposed to be “stretchable” so the content can be reflowed when the end user enters the values.
We would like something similar to this where we can have multiple lines if necessary and have it so the box will “stretch” or grow wider to allow all of the typed text to be shown.
output_app.docx (20.2 KB)
@amber.louk In your case, it is require to create cell level SDT. Please try modifying your IReplacingCallback
implementation like this:
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]);
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;
builder.InsertNode(sdtCheckBox);
}
else if (e.Match.Value == "___")
{
string sdtTitle = string.Format("text_{0}", mCounter++);
Cell matchCell = (Cell)e.MatchNode.GetAncestor(NodeType.Cell);
if (matchCell != null)
{
StructuredDocumentTag cellTextBox = new StructuredDocumentTag(builder.Document, SdtType.PlainText, MarkupLevel.Cell);
cellTextBox.Multiline = true;
cellTextBox.Title = sdtTitle;
// insert ST before the matched cell
matchCell.ParentNode.InsertBefore(cellTextBox, matchCell);
// Copy content form the created SDT cell into the matched cell
Cell innerCell = (Cell)cellTextBox.FirstChild;
matchCell.RemoveAllChildren();
while (innerCell.HasChildNodes)
matchCell.AppendChild(innerCell.FirstChild);
// Replace SDT cell with the matched cell to preserve formatting.
innerCell.Remove();
cellTextBox.AppendChild(matchCell);
}
else
{
StructuredDocumentTag plnTextBox = new StructuredDocumentTag(builder.Document, SdtType.PlainText, MarkupLevel.Inline);
plnTextBox.Title = sdtTitle;
builder.InsertNode(plnTextBox);
}
}
// 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;
}
When I try this, I receive an error about not being able to insert a null node.
@amber.louk Most likely you are using an old version of Aspose.Words. Could you please try using the latest 23.12 version of Aspose.Words on your side and let us know if the problem still persists?
I’m now getting an error saying “Cannot insert a node of this type at this location.”
@amber.louk Do you test with the same input document as attached in your initial post? The code works fine on my side. Here is the output document produced by the provided code:
Document doc = new Document(@"C:\Temp\in.docx");
FindReplaceOptions opt = new FindReplaceOptions(FindReplaceDirection.Backward);
opt.ReplacingCallback = new ReplaceWithSdtCallback();
doc.Range.Replace(new Regex("_[_X]_"), "", opt);
doc.Save(@"C:\Temp\out.docx");
out.docx (14.7 KB)