Skip to main content
A newer version of this page is available. .

Layout API

  • 6 minutes to read

This document introduces Layout API for the Rich Text Editor - objects, properties and methods that allow you traverse the document layout tree and access layout elements.

Layout API Overview

The Layout API is located in the DevExpress.RichEdit.v18.1.Core.dll assembly, and is available for both WinForms and WPF Rich Text Editor, as well as for Word Processing File API. It can be used to arbitrarily display layout elements and draw graphics within the element boundaries (expected in future versions) .

Layout API Object Model

The document layout model in the Layout API is a hierarchical tree-like structure. Each node in a tree is an instance of a certain class which implements a base interface named LayoutElement. A class representing a particular element is named after that element with addition of the prefix Layout or a suffix Box. There are LayoutPage, LayoutHeader, LayoutPageArea, LayoutFooter, … BookmarkBox, PlainTextBox etc. A layout element may or may not have a related RangedLayoutElement.Range in the document.

The structure of a sample document is shown in the picture below. The document consists of three pages, has a header and a footer, the first page has a floating text box and a floating picture. The text is organized in one column, the first page has only three lines of text. The first line of text includes a floating object’s anchor and five plain text boxes (a plain text box can be a space, a text with uniform formatting or a paragraph mark).

DocumentLayoutTree

Tip

Call the HitTestManager.HitTest method to obtain the RichEditHitTestResult instance containing information about the element located under the test point. Refer to the How to: Determine the Document Element under the Mouse Pointer topic for details.

Starting Point

The main entry point of the Layout API in WinForms Rich Editor is the RichEditControl.DocumentLayout property. This property provides access to the DocumentLayout object containing basic properties and methods for working with the hierarchy of the document layout objects. After any changes in text or formatting, the document layout is recalculated and the DocumentLayout.DocumentFormatted event fires.

Note

The DocumentFormatted event handler is running in a background thread. To avoid concurrency issues, execute the code which affects the document or interacts with the user asynchronously in a UI thread using the RichEditControl.BeginInvoke method.

Traversing Layout Tree

To get access to elements which constitute the document’s layout, create a Visitor object (that is, the object that implements a Visitor pattern) and visit a node of the document layout tree. Subsequently your visitor will traverse down the tree and visit every child node. For each node, it may call a specific method. At this time, you can only get information on the visited node. However, future API versions will allow you to hide the element, change the order in which elements are rendered or draw custom graphics.

The Visitor object must be a descendant of the LayoutVisitor abstract class. For each layout element, the LayoutVisitor class has a virtual method, thus the descending class must override this method to get access to an element of a particular type. The following code is a visitor class which gets access to every line of text in the main body of the document. Then, it prints visual coordinates and the document position at which the line starts. A line in the document is a LayoutRow object in terms of Layout API. The VisitRow method will be automatically called for each line.

A simple Visitor named MyDocumentLayoutVisitor is implemented in the following code:

class MyDocumentLayoutVisitor : DevExpress.XtraRichEdit.API.Layout.LayoutVisitor
{
    protected override void VisitRow(DevExpress.XtraRichEdit.API.Layout.LayoutRow row)
    {
        if (row.GetParentByType<DevExpress.XtraRichEdit.API.Layout.LayoutPageArea>() != null)
            System.Diagnostics.Debug.WriteLine("This row is located at X: {0}, Y: {1}, related range starts at {2}",
                row.Bounds.X, row.Bounds.Y, row.Range.Start);
        // Call the base VisitRow method to walk down the tree to the child elements of the Row.
        // If you don't need them, comment out the next line. 
        base.VisitRow(row);
    }

    protected override void VisitPage(DevExpress.XtraRichEdit.API.Layout.LayoutPage page)
    {
        System.Diagnostics.Debug.WriteLine("Visiting page {0}", page.Index +1);
        base.VisitPage(page);
    }


    }

To start traversing the tree, pass the node (in this situation, it is the document’s page, the LayoutPage object) of the document’s layout tree to the Visit method of the MyDocumentLayoutVisitor class. Handle the DocumentLayout.DocumentFormatted event and use the code below:

private void DocumentLayout_DocumentFormatted(object sender, EventArgs e)
{

    richEditControl1.BeginInvoke(new Action(() =>
    {
        int pageCount = richEditControl1.DocumentLayout.GetFormattedPageCount();
        for (int i = 0; i < pageCount; i++)
        {
            MyDocumentLayoutVisitor visitor = new MyDocumentLayoutVisitor();
            visitor.Visit(richEditControl1.DocumentLayout.GetPage(i));
        }
    }));
}

When the code is executed, the visitor traverses the tree and prints the coordinates of each row and related range.

Navigation with LayoutIterator

To step through elements of the document layout, use the LayoutIterator object. It navigates the layout tree and provides access to individual elements.

To start navigation, create an iterator using the LayoutIterator constructor and call LayoutIterator.MoveNext or LayoutIterator.MovePrevious method. The LayoutIterator.Current property obtains the current layout element.

Custom Draw

The RichEditControl.BeforePagePaint event allows you to specify a descendant of the PagePainter class to implement custom draw for layout elements.

The following image illustrates custom draw applied to the PlainTextBox and LayoutFloatingPicture elements.

Original document Custom Draw using PagePainter descendant
CustomDraw-Before CustomDraw-After
See Also