For the complete documentation index, see llms.txt. This page is also available as Markdown.

Traverse the content tree

Traversing the content tree, or, visiting every node or page in a hierarchical data structure makes the content tree stay inspectable until you paginate. This allows you to generate content first and decide its style afterwards which is useful for applying rules that depend on the finished document, such as banding every second row or restyling every heading.

Iterate children

GetContentNodeIterator returns an iterator over the direct children of a node. Every child is a ContentElement.

for (ContentNodeIterator itr = node.GetContentNodeIterator(); itr.HasNext(); itr.Next())
{
    ContentElement el = itr.Current();
    // ... inspect el ...
}
for (ContentNodeIterator itr = node.GetContentNodeIterator(); itr.HasNext(); itr.Next())
{
    ContentElement el = itr.Current();
    // ... inspect el ...
}
for itr := node.GetContentNodeIterator(); itr.HasNext(); itr.Next() {
    el := itr.Current()
    // ... inspect el ...
}
for (ContentNodeIterator itr = node.getContentNodeIterator(); itr.hasNext(); itr.next()) {
    ContentElement el = itr.current();
    // ... inspect el ...
}
const itr = await node.getContentNodeIterator();
while (await itr.hasNext()) {
    const el = await itr.current();
    await itr.next();
    // ... inspect el ...
}

Identify elements

A content element exposes one conversion method per concrete type. In C++, these return an ElementRef, which supports the safe-bool idiom and is dereferenced with ->. In the other languages, they return the object itself, or a null value when the element is not of that type.

Paragraph para = el.AsParagraph();
if (para != null)
    para.GetTextStyledElement().SetFontSize(11);
if (ElementRef<Paragraph> para = el.AsParagraph())
    para->GetTextStyledElement().SetFontSize(11);
para := el.AsParagraph()
if para.Swigcptr() != 0 {
    para.GetTextStyledElement().SetFontSize(11)
}
Paragraph para = el.asParagraph();
if (para != null)
    para.getTextStyledElement().setFontSize(11);
const para = await el.asParagraph();
if (para !== null) {
    (await para.getTextStyledElement()).setFontSize(11);
}
PTParagraph* para = [el AsParagraph];
if (para != nil)
    [[para GetTextStyledElement] SetFontSize:11];
$para = $el->AsParagraph();
if ($para != null)
    $para->GetTextStyledElement()->SetFontSize(11);
Method
Converts to

AsContentNode

Any node that has children

AsParagraph

Paragraph

AsTextRun

TextRun

AsPageNumber

PageNumber

AsTable, AsTableRow, AsTableCell

Table elements

AsList, AsListItem

List elements

AsShape, AsChart, AsFloat

Shape, chart and float

Info

In C++, dereferencing an invalid ElementRef throws an error. Always test it first.

Walk the whole tree

Recursing through AsContentNode visits every element in the document, whatever its nesting depth - including content inside table cells, list items, shape text boxes and floats.

A FlowDocument is itself a ContentNode, so passing the document walks the entire tree.

Restyle just-created content

Traversal is also handy for a narrow scope, such as recoloring the header row you have only just built.

Read content back

Traversal is not only for styling. The getters on text runs, tables, and sections let you inspect a document you have assembled, to compute a summary, run a validation pass, or log its structure.

Notes

  • Traversal reflects the tree as built. Line breaks, page breaks and page numbers are resolved during pagination and are not visible beforehand.

  • Iterators visit direct children only; recurse through AsContentNode to go deeper.

Further Reading

  • Tables for the table elements most often restyled after the fact.

  • Paragraphs and text for the style properties available on runs and paragraphs.

Last updated

Was this helpful?