How to replace the contents of a table cell

I am having problems finding an easy way to perform text replacement using Aspose.Words. Specifically I want to replace the contents of a table cell by a text string, preserving any existing formatting.

What is the best way to do this?

I thought I would be able to use cell.Range.Text = “My Text”, but the Range.Text property is readonly :frowning:

I tried clearing the Cells ChildNodes and adding a new paragraph, but this causes formatting of the cell to be lost.

In Word Automation I can achieve this easily, e.g.:

table.Rows(2).Cells(1).Select
wordApp.Selection.TypeText "My Text"

but I can’t find a simple way to do it using Aspose.

I’m also a bit confused that the Text property of the Bookmark object is read/write, but for a Range object it is readonly.

It is best to be done using Range.Replace method.

Here is an example:

string filename = Application.StartupPath + @"\Doc1.doc";
Document doc = new Document(filename);
NodeCollection tables = doc.GetChildNodes(NodeType.Table, true);
Range range = ((Table)tables[0]).Rows[0].Cells[0].Range;
range.Replace(new Regex(@"[\w ]\*"), "NewText");
doc.Save(Application.StartupPath + @"\Doc1\_modified.doc");

Regular expression @"[\w ]*" is used to replace only text that does not include any special characters. This precaution is required as cell always includes a special character ‘\a’.

That didn’t work for me: when I run it I get two copies of my new text inserted into the cell: “My Text My Text”.

I’m not much good at regular expressions, I also tried a regex without a space after the \w - this time I got four copies of my text.

Then I tried adding multiple paragraphs to the cell in the template file: what I wanted (the same as using the Word Automation technique) was for all paragraphs to be removed and replaced by one copy of my text. Instead I got “My Text” repeated several times in each of the paragraphs.

Next I tried inserting an image in the cell in the template file. Again, with Word automation the image is removed and replaced by the replacement text - this is not the case with Aspose.

Finally I tried adding a bookmark to the cell. I can then search for the bookmark and use Bookmark.Text = “My Text” - this gives the result I expect: all the cell contents (including e.g. images) are removed and replaced by a single copy of “My Text”, and the formatting is preserved.

But in my application it will be too much to ask users to insert a bookmark in every cell that has to be updated dynamically.

Is there not some general way to do this without using bookmarks?

I can’t understand why it is possible to replace text in this way with a Bookmark, but not with a Range: both objects essentially seem to represent an arbitrary sequence of nodes.

Unforunately current implementation of Replace method does not allow to replace text spanning across paragraph border. That means that you cannot replace several lines of text with one line using Replace. For the reasons of this please read the following entry in Roman Korchagin’s blog:

https://docs.aspose.com/words/net/find-and-replace/

As an alternative I can offer you another way of replacing cell text, yet preserving cell formatting:

string filename = Application.StartupPath + @"\Doc1.doc";
Document doc = new Document(filename);
DocumentBuilder b = new DocumentBuilder(doc);
// Use the document builder to move to the cell and insert text.
// When using document builder, the newly inserted text will take on
// formatting at the current position in the document.
b.MoveToCell(0, 0, 0, 0);
b.Writeln("New text");
//If the last call was Writeln, then the cursor is at the beginning
//of the first paragraph with the original text.
//The original text in the cell can be deleted in a loop like this.
Node node = b.CurrentParagraph;
while (node != null)
{
    Node nextNode = node.NextSibling;
    node.Remove();
    node = nextNode;
}
doc.Save(Application.StartupPath + @"\Doc1_modified.doc");

It does seem very convoluted for what seems to me to be a simple and mainstream requirement.

And I still can’t understand why I can do a Replace using a Bookmark, but not using a Range. It seems to be that if I could insert a BookmarkStart/End just before/after the Cell (or cell contents), I would be able to use Bookmark.Replace then Bookmark.Remove to achieve what I want. But I can’t work out how to insert a BookmarkStart/End at the right place.

Regarding the example you provided:: this still doesn’t do what I want:

  1. b.WriteLn(“text”) will insert text at the beginning of the cell. It will get the same formatting as the first paragraph already in the cell. I want the same result as a Replace using Word Automation - the output should have the same format as the cell itself (i.e. the last paragraph in the cell).

  2. Your sample does not show how to navigate to the cell. In my case I have a reference to a Table which I have located via a Bookmark. I don’t know the index of the Table. From what I can see, the steps I need to take are the following:

  • Find out which Story/Section my Table belongs to (How?)
  • Make this Story/Section the “current story”/“current section” (How?)
  • Iterate through the Tables collection of the Story containing my table to find the index of my table within the story
  • Now I can finally use MoveToCell to navigate to my cell.

I’ve worked it out, the following seems to work:

private void ReplaceCellText(Cell cell, string cellText)
{
    Node node = cell.LastParagraph;
    DocumentBuilder b = new DocumentBuilder(cell.Document);
    b.MoveTo(cell.LastParagraph);
    b.Writeln("");
    b.Write(cellText);

    while (node != null)
    {
        Node nextNode = node.PreviousSibling;
        node.Remove();
        node = nextNode;
    }
}

Great to hear. We also thought about the workaround with inserting a bookmark and then setting its text. It is possible, but the text assumes formatting that is at the beginning of the bookmark (like in MS Word).

Document doc = TestUtil.Open(@"Model\Table\TestReplaceCell.doc");
Cell cell = doc.Sections[0].Body.Tables[0].Rows[0].Cells[0];
// "Insert after null" actually inserts before the first child.
cell.FirstParagraph.InsertAfter(new BookmarkStart(doc, "bmk"), null);
cell.LastParagraph.AppendChild(new BookmarkEnd(doc, "bmk"));
Bookmark bmk = cell.Range.Bookmarks["bmk"];
bmk.Text = "New text";
TestUtil.Save(doc, @"Model\Table\TestReplaceCell1 Out.doc");
cell = (Cell)doc.GetChild(NodeType.Cell, 0, true);
Assert.AreEqual("New text\a", cell.GetText());

Thanks again for the responsive support.

I can live with this - replacing text is not as intuitive as I’d hoped, but I can easily encapsulate the complexity in a method like ReplaceCellText.