Layout API
- 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.v24.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).
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 Direction | Can Iterate Only a Specific Layout Level | Visits Complex Layout Elements | |
---|---|---|---|
LayoutVisitor | Down | No | True |
LayoutIterator | Up 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
//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));
Use LayoutVisitor
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);
}
}
}
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 |
---|---|