Viewing, Zooming, and Navigation
Whether the editor shows pages like a printout or text that flows with the width of the form, how large it draws them and where the user is in a long document are settings of the TextControl. Users change them in the View tab of the ribbon and the status bar; your code sets them with a few properties, moves the input position to a page and reacts when the user zooms or turns a page.
The code runs in Form1.cs with a TextControl named textControl1, the editor of Create Your First Windows Forms Application.
Choose a View Mode
ViewMode decides how the document is laid out on the screen:
| Value | The editor shows |
|---|---|
PageView | Pages with gaps and a desktop background, formatted with the page size and margins; headers and footers are displayed |
Normal | Text formatted with the page size and margins, without the page view; headers and footers can be inserted but aren't displayed |
FloatingText | Text as wide as the control, with a vertical scroll bar for unlimited text; the page size, margins, headers and footers are ignored |
SimpleControl | Text formatted to the width and height of the control; when it is full, no more text can be entered |
PageView is the view of a word processor and the only mode in which the editor counts pages. The following code sets it when the form opens:
private void Form1_Load(object sender, EventArgs e)
{
// Show pages like a printed document
textControl1.ViewMode = ViewMode.PageView;
}
In page view, BackgroundStyle chooses the background around the pages: ColorScheme uses the color scheme of the system or the colors of DisplayColors, and ClassicColors a dark gray. ScrollBars takes a value of the System.Windows.Forms.ScrollBars enumeration: None, Horizontal, Vertical or Both.
In SimpleControl mode, the control can also change its own width or height as the user types, like a text box that grows with its text. Let the Control Grow With Its Content shows the AutoControlSize settings and events for this.
Zoom the Document
Users zoom with the slider of the status bar, the zoom items of the View tab or Ctrl and the mouse wheel. In code, ZoomFactor sets and returns the zoom factor in percent, and Zoom zooms to a factor or to a value of ZoomOption:
private void zoomInButton_Click(object sender, EventArgs e)
{
textControl1.Zoom(textControl1.ZoomFactor + 10);
}
private void pageWidthButton_Click(object sender, EventArgs e)
{
// Fit the width of the page into the control
textControl1.Zoom(ZoomOption.PageWidth);
}
ZoomOption.PageWidthfits the width of a page into the control,ZoomOption.WholePagethe current page, andZoomOption.TextWidththe text of a line.- In
PageViewandNormalmode, the factor can be from 10 up toushort.MaxValuepercent. In the other modes, it can be from 10 to 400 percent. - After the zoom factor has changed, the
TextControlraises Zoomed. A handler can show the new factor:
private void textControl1_Zoomed(object sender, EventArgs e)
{
zoomLabel.Text = $"{textControl1.ZoomFactor} %";
}
zoomLabel is a Label on the form. The blog article Zoom Tricks: Disabling CTRL + MOUSE WHEEL and More turns off zooming with the mouse wheel and adds keyboard shortcuts for zooming.
Show Formatting Marks and Markers
Some characters and objects are invisible in a printed document but help while editing:
| Property | Shows |
|---|---|
| ControlChars | All control characters, such as spaces, tabs and paragraph ends |
| PermanentControlChars | Selected control characters all the time, as a combination of PermanentControlChar values such as ParagraphEnd, ForcedPageBreak or SectionBreak |
| TextFrameMarkerLines | Marker lines around text frames without a border |
| DrawingMarkerLines | Marker lines around drawings |
| DocumentTargetMarkers | Markers at hypertext targets, such as bookmarks |
Users don't need code for these settings: the View tab, a RibbonViewTab, switches between Print Layout and Draft in its Document Views group, sets the zoom factor or fits the page, its width or the text width into the control in its Zoom group, shows and hides the rulers and the status bar in its Toolbars group, and turns table gridlines, bookmark markers, marker lines, control characters and frame anchors on and off in its Show group.

In code, a combination of permanent control characters is written with the bitwise OR operator:
// Always show paragraph ends and page breaks
textControl1.PermanentControlChars = PermanentControlChar.ParagraphEnd | PermanentControlChar.ForcedPageBreak;
Go to a Page
The input position has a page, a line and a column. Setting InputPosition to a new InputPosition moves the caret there, and ScrollTo scrolls it into view:
private void goToPageButton_Click(object sender, EventArgs e)
{
int page = 3;
// Pages exist only in page view
if (textControl1.ViewMode != ViewMode.PageView || page > textControl1.Pages)
{
return;
}
// Line 1, column 0 is the start of the page
textControl1.InputPosition = new InputPosition(page, 1, 0);
textControl1.InputPosition.ScrollTo(InputPosition.ScrollPosition.Top);
textControl1.Focus();
}
Pages returns the number of pages of the document. Selections and Caret Position describes the InputPosition constructor and ScrollTo in detail.
GetPages returns a PageCollection with a Page object per page, and null outside page view. Page.Select selects the text of a page, without its header and footer. For users, the GotoDialog is a modeless dialog box that moves the input position to a page, section, line, table or bookmark:
private void goToDialogButton_Click(object sender, EventArgs e)
{
GotoDialog dialog = new GotoDialog(textControl1);
dialog.Show();
}
React to the Position in the Document
PageChanged occurs when the input position moves to another page. InputPosition.Page then returns the new page, in page view only:
private void textControl1_PageChanged(object sender, EventArgs e)
{
pageLabel.Text = $"Page {textControl1.InputPosition.Page} of {textControl1.Pages}";
}
The StatusBar of TX Text Control shows the same information without code; see Rulers and Status Bar. HScroll and VScroll occur when the user scrolls, and ScrollLocation returns or sets the upper-left corner of the visible part of the document, in twips.
Scroll to a Text Position Without Moving the Caret
InputPosition.ScrollTo moves the caret first. To bring a text position into view and leave the caret where it is, set ScrollLocation to the position of the character:
private void ScrollToTextPosition(int textPosition)
{
Point location;
// TextChars counts from 1, text positions from 0
if (textPosition + 1 <= textControl1.TextChars.Count)
{
location = new Point(0, textControl1.TextChars[textPosition + 1].Bounds.Y);
}
else
{
// Past the end: scroll to the last line
location = new Point(0, textControl1.Lines[textControl1.Lines.Count].TextBounds.Y);
}
textControl1.ScrollLocation = location;
}
The code works as follows:
- The text position is zero-based, as Selection.Start and InputPosition.TextPosition are. The indexer of TextChars counts characters from 1, so the character at a text position is
TextChars[textPosition + 1]. - TextChar.Bounds and Line.TextBounds are given in twips from the upper-left corner of the document, the same coordinates as
ScrollLocation. - A position beyond the last character scrolls to the last line, which
Lines[Lines.Count]returns from the one-based Lines collection.
Point is the System.Drawing.Point structure.
Show the Page Number of the Visible Page
In a read-only viewer, users scroll instead of moving the caret, so InputPosition.Page and PageChanged stay on the page where the caret is. The page the user looks at is the page of the line in the middle of the control. LineCollection.GetItem with a Point, in pixels from the upper-left corner of the control, returns the line displayed there, taking the scroll position into account. Update the display in the VScroll event:
private void textControl1_VScroll(object sender, EventArgs e)
{
// The line in the middle of the visible part
Line line = textControl1.Lines.GetItem(new Point(0, textControl1.Height / 2));
if (line == null || textControl1.ViewMode != ViewMode.PageView)
{
return;
}
int section = textControl1.GetPages()[line.Page].Section;
pageLabel.Text = $"Page {line.Page} of {textControl1.Pages}, section {section} of {textControl1.Sections.Count}";
}
Line.Page is the number of the page, counted from 1. GetPages()[number] returns that Page, and Page.Section the number of its section; when a page holds several sections, it is the first one, which sets the size and orientation of the page.
Next Steps
- Selections and Caret Position: read and set the input position and the selection.
- Sections and Page Layout: page sizes, margins and orientation that page view shows.
- Rulers and Status Bar: the zoom slider and the page display of the status bar.
- Printing and Print Preview: from the screen to paper.