ABOVE: A TextRuler control is placed above host RichTextBox and Label controls.
Last updated: February 18th, 2019
TextRuler is a
UserControl that can be integrated with any rich-text-box control to allow the user to create, remove, and move margins, indents, and tabs for the rich-text control.
- It allows the programmer to set the effective width of the ruler (not to be confused with the control's width!), as well as what kind of markers (margins, indents, tabs) can be manipulated by the user by clicking/dragging.
- It has properties and events that allow synchronization of margin/indent/tab values of a rich-text control and those of this control.
- The ruler can be configured to measure in inches (tick marks every 1/8 in) or centimeters (tick marks every 5 mm [1/2 cm]).
- It supports tooltips, including default "special" tooltip text for whenever the user positions the mouse over a marker.
- It supports dealing with whether the display should be scrolled in order to make sure that its markers line up with the host rich-text control's margin/indent/tab values.
- NEW! It can be scaled by a
ZoomFactor to synchronize with such enlargement/shrinkage of text in a rich-text control.
This control was derived from the "
UserControl of Aleksei Karimov's Advanced Text Editor with Ruler, with the ruler-control's code converted to VB.NET and with numerous modifications and enhancements. This new version is designed to be a
general-purpose "stand-alone" which can then be added/integrated to
UserControl or project.
Using the Code
Properties for TextRuler
Properties are as follows:
MarkerUnderMouse / TabUnderMouse -- MerkerUnderMouse returns a
MarkerType enumeration value indicating what kind of marker the mouse is currently positioned over. If it's a Tab, then
TabUnderMouse returns the position in pixels for the tab under the mouse.
sets whether or not the ruler will display, or allow the user to set, tab stops
Single any array of tab positions in pixels (
TabPositions) or the current
Units-property type (
TabPositionsInUnits). The comparable standard
RichTextBox property to
PixelsPerInch -- Returns the number of pixels per centimeter/inch for use with making conversions; these values are
Single in order to facilitate accurate conversions for large values.
UnitType enumeration value for the current type of units to display the ruler using; either
Inches (default) or
sets the control's tooltip
True, then default descriptive names will display whenever the mouse is over a margin/indent/tab marker.
sets whether the tooltip text should describe the marker type whenever the mouse is over one. If you want to specify for yourself what kind of tooltips are used for markers, set this value to
False and use the
MarkerUnderMouse property in the event code to determine what the user's over.
sets the color for the ruler's border or background within the border.
sets how much horizontal space in pixels--with/without any margins--should be handled by the ruler. The comparable
RichTextBox property to
PrintableWidth is the rich-text control's
RightMargin property (when that property is non-zero).
PrintableWidth plus any left and right margin space. (NOTE: These are not the
sets how many pixels after the start of the ruler to start drawing it and tracking markers on it. This is to allow the ruler markers to line up with the rich-text whenever the text is horizontally scrolled.
sets whether the ruler should contain space on the left and/or right that is off-limits to markers being set or dragged to. Setting this value to
True sets the
RightMargin properties to zero. (For the basic
RichTextBox control, which doesn't display off-limits space, set this value to
sets how many pixels at the left / right side of the ruler should be off-limits for markers.
NoMargins must be
False for in order to be able to set these properties. (NOTE: The
RightMargin property here measures leftward from the right edge of the ruler, whereas the
RightMargin property of the
rightward from the
left edge of a line of text.)
sets how many pixels after the left margin the first line of a paragraph should start. The difference with these properties is when they are set: Setting
FirstLineIndent changes the position of
only the first line of paragraph text while leaving subsequent lines at their current positions (and
HangingIndent is adjusted accordingly), while setting
all lines an equal distance left or right (
HangingIndent remains the same). The comparable
RichTextBox property is
sets how many pixels after a paragraph's first-line indent the indentation of subsequent paragraph-lines should be. A
negative value makes subsequent lines in a paragraph
less indented than the first, a
positive value makes them
more indented than the first line, and
zero lines them all up. The comparable
RichTextBox property is
sets how many pixels before the right margin a paragraph line should stop at. The comparable
RichTextBox property is
- ZoomFactor -- NEW! Gets or
sets how much to enlarge or shrink the ruler display; corresponds to the
RichTextBox property of the same name. As with the
ZoomFactor only affects the display size of margins, indents, and tabs--the pixel/unit values of the associated properties are what they would be if
ZoomFactor was 1. That is, their positions when set are scaled to the
ZoomFactor for the display, and de-scaled when read from the display. The
PrintableWidth properties, like the
RightMargin property, are not affected by the scaling, however, so scaling the display up/down will decrease/increase the logical range of the ruler, just as zooming in the
RichTextBox affects how much physical text will fit on a line.
Methods for TextRuler
Methods as follows:
PixelsToUnits(pixels) -- Converts a given number of pixels to their equivalent in inches or centimeters, depending on the
Units property's value.
UnitsToPixels(units) -- Converts a given number of inches or centimeters, depending on the value of the
Units property, to their equivalent in pixels.
Events for TextRuler
Events as follows:
IndentsChanged/ MarginsChanged -- fire whenever the user changes an indent's or margin's value
MarkerType value for the kind of indent or margin was moved
TabAdded / TabRemoved / TabChanged -- fire whenever the user adds, removes, or moves, respectively, a tab position.
TabEventArgs.OldPosition -- original position in pixels of tab that was moved or deleted
TabEventArgs.NewPosition -- new position in pixels of tab that was moved or added
These events only fire when the
user changes a margin/indent/tab--not when the programmer changes these values by code. Also, they fire after a change is already made, and therefore don't directly support cancellation of changes.
Sample Code Snippets
A few code examples are given below:
Private Sub RTBToRuler()
TextRuler1.NoMargins = True : TextRuler1.PrintableWidth = RichTextBox1.RightMargin
TextRuler1.Units = MarkerType.Inches
TextRuler1.ScrollingOffset = TextRuloer1.UnitsToPixels(1/4)
TextRuler1.FirstLineIndent = RichTextBox1.SelectionIndent
TextRuler1.HangingIndent = RichTextBox1.SelectionHangingIndent
TextRuler1.RightIndent = RichTextBox1.SelectionRightIndent
TextRuler1.TabPositions = RichTextBox1.SelectionTabs
TextRuler1.ZoomFactor = RichTextBox1.ZoomFactor
Private Sub RulerToRTB()
RichTextBox1.SelectionIndent = TextRuler1.FirstLineIndent
RichTextBox1.SelectionHangingIndent = TextRuler1.HangingIndent
RichTextBox1.SelectionRightIndent = TextRuler1.RightIndent
RichTextBox1.SelectionTabs = TextRuler1.TabPositions
Public Sub IndentsChanged(sender As Object, e As MarginOrIndentEventArgs) _
Dim s As String = e.MarkerType.ToString.Replace("_", " ") _
& " changed. Right Indent = " _
& TextRuler1.PixelsToUnits(TextRuler1.RightIndent).ToString _
If TextRuler1.Units = UnitType.Inches Then
s &= " inches"
s &= " centimeters"
Label1.Text = s
The sample application shows a rich text box with a ruler above it and a label below it (see top of article); the latter shows the current indent and tab positions in inches or centimeters. (Right-click the ruler to choose between units.) The form's code module inches a
Module inside of which an extension method,
GetScrollPosition is defined for getting the horizontal scroll position whenever text in the text box is scrolled so that the ruler's markers are horizontally synchronized accordingly. (It uses an unmanaged Win32 API,
SendMessage.) Finally, I should note that the program accounts for whether or not the rich-text control shows an 8-pixel "selection margin" left of any text when aligning the ruler to it.
Points Of Interest
- The control will
not allow the user or the programmer to set margins, indents, and tabs in such a way that indents or tabs are outside the "printable-width" area (that is, out in the margins' space) or the rightmost left indent and the right indent are jammed together. Also, it won't let one create 2 or more tab positions that overlap or move existing tabs out of order (that is, move one tab over and past another). These rules are partly to keep the control from confusing one marker for another when tracking the mouse, and partly for the sake of coding simplicity. To allow for "margin release"--in which tabs or indents can appear in the margins--
carefully study the source code. All information is internally stored in pixels with indents represented in absolute distance from the left/right ends (including the margins) and tabs represented relative to the scrolling offset and zoom factor. (NOTE: No exception is thrown if the user/programmer tries to set a value to something unacceptable; instead the attempted change is simply ignored.)
- If you want to allow program code to "cancel" user changes in advance, I recommend creating 5 additional events that fire before the change and use argument classes that have the same properties as
TabEventArgs, but which inherit from
- I've recently updated my own text-editor
UserControl, EXTENDED Version of Extended Rich Text Box (RichTextBoxEx), to incorporate this ruler as a constituent control.