Back to Devexpress

Layout API

windowsforms-114069-controls-and-libraries-rich-text-editor-page-layout-layout-api.md

latest9.8 KB
Original Source

Layout API

  • Apr 15, 2025
  • 5 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.v25.2.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) .

View Example: Document Layout API - Practical Usage

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. In this case, a class is a particular element that is named after an element with addition of the prefix Layout or a suffix Box : 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).

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

The LayoutIterator and LayoutVisitor allow you to collect information about document element’s location (bounds, nearest element, etc.) and range.

The table below compares LayoutVisitor and LayoutIterator features. This comparison allows you to decide what to use depending on your current task.

Travel DirectionCan Iterate Only a Specific Layout LevelVisits Complex Layout Elements
LayoutVisitorDownNoTrue
LayoutIteratorUp and Down (using LayoutIterator.MoveNext and LayoutIterator.MovePrevious methods).Yes. Pass the one of the LayoutLevel values to the Move.. method to complete the task.False. Create a new LayoutIterator when visiting complex elements (header/footer, floating objects, comments)

Code samples below show how to use LayoutIterator and LayoutVisitor to count lines in the document’s main body. The message box displays the result amount, as shown on the image.

Use LayoutIterator

csharp
//Create a new LayoutIterator object
LayoutIterator iterator = new LayoutIterator(richEditControl1.DocumentLayout);
int documentRows = 0;

    //Call the MoveNext method with the passed LayoutLevel.Row value
    //to process page lines:
    while (iterator.MoveNext(LayoutLevel.Row))
    {
       //Count visited lines until the method returns false
       documentRows++;
    }

//Show the result number in the message box
MessageBox.Show(String.Format("The document contains {0} lines", documentRows));
vb
'Create a new LayoutIterator object
Dim [iterator] As New LayoutIterator(richEditControl1.DocumentLayout)

'Call the MoveNext method with the passed LayoutLevel.Row value
'to process page lines:
Dim documentRows As Integer = 0
      Do While [iterator].MoveNext(LayoutLevel.Row)
            documentRows += 1
       Loop
'Show the result number in the message box:
MessageBox.Show(String.Format("The document contains {0} lines", documentRows))

Use LayoutVisitor

csharp
int rowCount = 0;
int pageCount = richEditControl1.DocumentLayout.GetPageCount();

//Create a new <b>Visitor</b> object for every document page
for (int i = 0; i < pageCount; i++)
 {
   MyLayoutVisitor visitor = new MyLayoutVisitor(); 

   //Call the Visit method for the current layout page and count its lines
   visitor.Visit(richEditControl1.DocumentLayout.GetPage(i));
   rowCount += visitor.DocumentRows;
 }

//Show the result in the message box 
MessageBox.Show(String.Format("The document contains {0} lines", rowCount));

//Create a LayoutVisitor class descendant: 
public class MyLayoutVisitor : LayoutVisitor
{
  public int DocumentRows = 0;

  //Override the <b>VisitRow</b> method to count lines 
  //located only in main document body,
  //i.e. lines located on the LayoutPageArea level
   protected override void VisitRow(LayoutRow row)
   {
     if (row.GetParentByType<LayoutPageArea>() != null)
     {
       DocumentRows++;
       base.VisitRow(row);
     }
   }
}
vb
Dim rowCount As Integer = 0
Dim pageCount As Integer = richEditControl1.DocumentLayout.GetPageCount()

'Create a new Visitor object for every document page
For i As Integer = 0 To pageCount - 1
  Dim visitor As MyLayoutVisitor = New MyLayoutVisitor()

  'Call the Visit method for the current layout page and count its lines
  visitor.Visit(richEditControl1.DocumentLayout.GetPage(i))
  rowCount += visitor.DocumentRows
   Next
'Show the result in the message box 
MessageBox.Show(String.Format("The document contains {0} lines", rowCount))

'Create a LayoutVisitor class descendant:
Public Class MyLayoutVisitor
    Inherits LayoutVisitor

    Public DocumentRows As Integer = 0
    'Override the VisitRow method to count lines 
    'located only in main document body,
    'i.e. lines located on the LayoutPageArea level
    Protected Overrides Sub VisitRow(ByVal row As LayoutRow)
        If row.GetParentByType(Of LayoutPageArea)() IsNot Nothing Then
            DocumentRows += 1
            MyBase.VisitRow(row)
        End If
    End Sub
End Class

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 documentCustom Draw using PagePainter descendant

See Also

DocumentLayout

Document Layout