Click here to Skip to main content
13,800,219 members
Click here to Skip to main content
Add your own
alternative version


13 bookmarked
Posted 18 Jan 2018
Licenced CPOL

General-purpose RULER Control for Use with RICH-TEXT CONTROLS

, 31 Jan 2018
Rate this:
Please Sign up or sign in to vote.
A UserControl that allows rich-text applications to have a ruler with support for margins, indents, and tabs

Most recently updated: February 1, 2018

ABOVE: A TextRuler control is placed above host RichTextBox and Label controls.


This 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

  1. 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.
  2. It has properties and events that allow synchronization of margin/indent/tab values of a rich-text control and those of this control.
  3. 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]).
  4. It supports tooltips, including default "special" tooltip text for whenever the user positions the mouse over a marker.
  5. 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.

This control was derived from the "TextRuler" UserControl of '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 any rich-text-based 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.
  • TabsEnabled -- Gets or sets whether or not the ruler will display, or allow the user to set, tab stops
  • TabPositions / TabPositionsInUnits -- Gets or sets any Integer / Single any array of tab positions in pixels (TabPositions) or the current Units-property type (TabPositionsInUnits). The comparable standard RichTextBox property to TabPositions is SelectionTabs.
  • PixelsPerCentimeter / 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.
  • Units -- Gets or sets a UnitType enumeration value for the current type of units to display the ruler using; either Inches (default) or Centimeters.
  • ToolTipText -- Gets or sets the control's tooltip String. If UsingSmartTooltips is True, then default descriptive names will display whenever the mouse is over a margin/indent/tab marker.
  • UsingSmartToolTips -- Gets or 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.
  • BorderColor / BaseColor -- Gets or sets the color for the ruler's border or background within the border.
  • RulerWidth / PrintableWidth -- Gets or 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). RulerWidth is PrintableWidth plus any left and right margin space. (NOTE: These are not the control's width.)
  • ScrollingOffset -- Gets or 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.
  • NoMargins -- Gets or 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 LeftMargin and RightMargin properties to zero. (For the basic RichTextBox control, which doesn't display off-limits space, set this value to True.)
  • LeftMargin / RightMargin -- Gets or 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 RichTextBox measures rightward from the left edge of a line of text.)
  • FirstLineIndent / LeftIndent -- Gets or 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 LeftIndent moves all lines an equal distance left or right (HangingIndent remains the same). The comparable RichTextBox property is SelectionIndent.
  • HangingIndent -- Gets or 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 SelectionHangingIndent.
  • RightIndent -- Gets or sets how many pixels before the right margin a paragraph line should stop at. The comparable RichTextBox property is SelectionRightIndent.

Methods for TextRuler

Methods as follows:

  • value = PixelsToUnits(pixels) -- Converts a given number of pixels to their equivalent in inches or centimeters, depending on the Units property's value.
  • 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


    • MarginOrIdentEventArgs.MarkerType -- 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:

Imports TextRuler

'   getting info from rich-text box

Private Sub RTBToRuler()
TextRuler1.NoMargins = True : TextRuler1.PrintableWidth = RichTextBox1.RightMargin
TextRuler1.Units = MarkerType.Inches
TextRuler1.ScrollingOffset = TextRuloer1.UnitsToPixels(1/4) 'scroll past first quarter-inch
TextRuler1.FirstLineIndent = RichTextBox1.SelectionIndent
TextRuler1.HangingIndent = RichTextBox1.SelectionHangingIndent
TextRuler1.RightIndent = RichTextBox1.SelectionRightIndent
TextRuler1.TabPositions = RichTextBox1.SelectionTabs
End Sub

'   setting info into rich-text box

Private Sub RulerToRTB()
RichTextBox1.SelectionIndent = TextRuler1.FirstLineIndent
RichTextBox1.SelectionHangingIndent = TextRuler1.HangingIndent
RichTextBox1.SelectionRightIndent = TextRuler1.RightIndent
RichTextBox1.SelectionTabs = TextRuler1.TabPositions
End Sub

'   event

Public Sub IndentsChanged(sender As Object, e As MarginOrIndentEventArgs) _
   Handles TextRuler1.IndentsChanged
'   display what was changed and current right-indent value in current units
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"
End If
Label1.Text = s
End Sub

Demo Program

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 chose 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

  1. 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 less than 11 pixels apart. (That means that the ruler must allow for at least 11 pixels of "printable" text.) Also, it won't let one create 2 or more tab positions within 11 pixels of each other 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 the scrolling offset. (NOTE: No exception is thrown if the user/programmer tries to set a value to something unacceptable; instead the attempted change is simply ignored.)
  2. This ruler control is not designed to be "scaled" up or down (too much coding trouble to implement!) alongside the text in a rich-text box. The text and the markers, therefore, only line up when the latter control has a ZoomFactor value of 1.0.
  3. 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 MarginOrIndentEventArgs and TabEventArgs, but which inherit from CancelEventArgs.
  4. 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.


This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


About the Author

Robert Gustafson
United States United States
No Biography provided

You may also be interested in...


Comments and Discussions

-- There are no messages in this forum --
Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web04 | 2.8.181214.1 | Last Updated 1 Feb 2018
Article Copyright 2018 by Robert Gustafson
Everything else Copyright © CodeProject, 1999-2018
Layout: fixed | fluid