Download RichTextBoxEx UserControl, with TextRuler UserControl, PrintRichTextBox class library, NetSpell DLL, and Demo Application
RichTextBox of Visual Studio allows a lot of things; the problem is, a host program has to initiate those things in code. The following control,
RichTextBoxEx, which features a toolbar and ruler, allows elaborate end-user-initiated functionality in a single
WinForms control. Below is the
RichTextBoxEx control, plus
CheckBoxes to enable/disable spell-check and picture insertion and a multi-line
TextBox to show the main control's width, scroll position, and character code for any selected text.
This UserControl is a WinForms RichTextBox with a Toolstrip and Ruler added to allow the user to do the following things with it (some updates are recent--as of December 4, 2018):
- Setting (foreground and background), attributes, text alignment
- Finding and replacing search text
- Defining custom characters and smart-character conversions
- Saving and loading text
- Printing (using class library)
- Tracking scroll-bar information (using class library)
- Keeping track of whether recent changes have been made
- Setting tabs indents and tabs via a ruler
- Text listing (non-nested bulleted, numbered, and lettered lists)
- Symbol-inserting (unicode characters)
This tip features the RichTextBoxEx control--an enhanced version of the "Extended RichTextBox" created by Razi Syed--along with Paul Welter's "NetSpell - Spell Checker for .NET" (DLL and dictionary files only)--plus a class library for printing rich text, getting scroll-bar info, and setting list-styles (standard or enhanced control), a ruler control specially designed for rich-text boxes, and a small demo program. This version has last been updated on December 4, 2018.
This version of RichTextBoxEx has the following enhancements to Razi Syed's version:
- Buttons for Background Color, Italic, Insert Picture, Find/Replace, Find Next, Hyphenate, and Insert Symbol--and combo boxes for face and size--have been added to the toolstrip. Also, when the control is resized, the constituent controls--toolstrip and text box--are resized to fill it.
- The underlying RichTextBox, "rtb", is declared Public so that its properties, methods, and events can be accessed; just be sure to include "rtb" in the object expression.
- The underlying "rtb" control has its AllowDrop property set to True, so that rich text can easily be dragged and dropped back and forth with other rich-text apps.HideSelection is set to False so selected text can be viewed while find, replace, and hyphenation dialogs are showing. ShowSelectionMargin is set to True so that the user can easily select whole lines of text.
- A context menu with shortcut keys is assigned to the "rtb" control.
- The constituent rich-text control's internal KeyDown event defines several "custom characters"--and raises an event to allow you to define your own custom characters or shortcuts; its internal KeyPress event defines "smart characters"--and raises an event to allow you to define your own smart-character replacements. (The text boxes for the Find/Replace dialog box recognize the default custom-character keystrokes now.)
- There are top-level properties for allowing/disallowing spell-check/bullets/picture-insertion/insert-text/smart-text by the user, showing/hiding the toolstrip (items' shortcut keys and any custom characters still work), allow/disallow setting text and together--and, as previously mentioned, an event to allow custom key sequences to generate RTF characters/strings.
- To search for text, use Find/Replace (Ctrl + F / Ctrl + H) to pull up a dialog box for entering search text, search direction (up or down), and whether or not to make search case-sensitive and/or whole word. If found in the rich-text, the word is selected. If opting to replace, you can replace one occurrence or all occurrences from the current position in the specified direction. To exit the Find or Replace dialog, hit Cancel (ESC). To find and select subsequent occurrences of the same text (using the same direction and criteria), use Find Next (F3).
- Pictures can be added. When the Insert Picture button (or the equivalent context-menu item) is selected, a file dialog allows the user to select a picture file. The picture is copied to the clipboard and pasted into the control at the current caret position (replacing any pre-highlighted text).
- The control can search for hyphenateable wrapped text; when such a word is found, a dialog box allows one to position the breaking point, and either hyphenate the word, skip it (leave it alone), or quit. (The word must be a series of alphanumeric characters with enough length/space to leave 2+ characters at the end of the first line and 3+ characters at the beginning of the second line after breaking.)
- The control can dehyphenate text: The options to hyphenate text, remove all hyphens, or remove only "hidden" hyphens (hyphens not displayed because text doesn't wrap after them) are in the "Hyphenate" context menu.
- A class library is added to facilitate printing of rich text, and tracking of the text box's scroll-bar info. The first 5 extension methods can be used to find page breaks within the text box, and to print or preview one, some, or all pages; the other 3 methods can be used to get or set the scroll-bars' positions or retrieve detailed information about either scroll bar.
- Three methods are created: 1 for saving RTF text, 1 for loading RTF text, and 1 for inserting pictures
- A ruler can be displayed to allow the user to set first-line/hanging/right indents and tabs.
- Lists can be specified as bulletted, Arabic-numbered, upper-case/lower-case Roman numeraled, or upper-case/lower-case lettered. (Lists cannot be "nested"--i.e., for outlining. Unfortunately.)
- A dialog exists for finding an inserting symbols that can be represented by printable Unicode characters in various fonts.
Using the Code
Control's Reference Dependencies
To use the
RichTextBoxEx UserControl in a project/solution, you must first reference RichTextBoxEx.dll, and, if you want to allow spell-checking, NetSpell.SpellChecker.dll. Also, if you plan to use the extension methods of the PrintRichTextBox class library in the host application's code, then you need to
Import RichTextBoxEx or
Import RichTextBoxEx.PrintRichTextBox in any code files using the methods.
This version of RichTextBox.dll incorporates the main control (RichTextBoxEx), together with the TextRuler constituent control, and the PrintRichTextBox library, all into 1 single project--eliminating the need for as many references as before.
Each of these supporting libraries--PrintRichTextBox, TextRuler, and NetSpell--can be used on their own; to find out how to use the
TextRuler, see the article General-purpose RULER Control for Use with RICH-TEXT CONTROLS by yours truly.
Also, if you already are using this control, note that the
AllowBullets has been replaced by
AllowLists. If you want your pre-existing programs to run or even display the (new version of the) control on the projects' forms, you must change all references to "AllowBullets" to "AllowLists" in all code, including the forms' "designer-code" files! (Simply use the Replace text option with the scope set to "Entire Solution"; or display Error box and click on errors about this to get to and fix the antiquated statements one at a time.)
Underlying Text Box Events
rtb, is defined as public so you can access its properties, methods, and events. To catch events for the underlying
rtb, use AddHandler and RemoveHandler to hook procedures to them; using WithEvents and Handles doesn't work with constituent controls! The last line of the code sample below shows an example.
Properties for RichTextBoxEx
MaintainSelection -- Boolean for whether highlighting should be restored to selected text after a replace, hyphenation, or spell-check operation
IsTextChanged --Boolean for whether text has been changed since it was saved, loaded, or last set to False (set to True to assume changes, False to indicate no recent changes). It is similar to the underlying rich-text box's
Modfied property except that it is set to True, and the
ChangesMade event is fired,when the
Text property is set (or
Rtf's value is changed), and not when "temporary" changes (like trial hyphenations) are made.
SetColorWithFont -- Boolean for whether it should be possible to set text from within the Font dialog
ShowToolstrip -- Boolean for whether to show Toolstrip on top of RichTextBox
ShowRuler -- Boolean for whether to show a ruler for setting indents and tabs
AllowTabs -- Boolean for whether the user is able to set tabs for paragraphs (only meaningful if ShowRuler is True)
AllowSpellCheck -- Boolean for whether control should provide for spell-checking
AllowDefaultInsertText -- Boolean for whether control should provide automatic default custom-character insertions along with any user-defined ones
AllowDefaultSmartText -- Boolean for whether control should provide automatic default smart-character replacments along with any user-defined ones
AllowLists (NEW!) -- Boolean for whether control should allow list bulleting (replaces
AllowBullets; see Note above)
AllowHyphenation -- Boolean for whether control should support hyphenation searches
AllowPictures -- Boolean for whether control should allow pictures to be inserted
AllowSymbols (NEW!) -- Boolean for whether control should allow custom symbols to be inserted using Insert Symbol dialog
TextRuler.Units enumeration for whether Inches or Centimeters should be used for the ruler (
Import RichTextBoxEx or
Import RichTextBoxEx.TextRuler into any code files that use this property!)
RightMargin -- maximum printable width (
rtb.RightMargin) for text; the ruler display is updated to reflect the change
FilePath -- String for directory the file dialog should start with (when saving text, loading text, or inserting pictures)
rtb -- constituent RichTextBox control
Text -- String for plain-text contents of text box (
rtb.Modified are set to True and
ChangesMade event is fired always
Rtf -- String for RTF contents of text box (
rtb.Modified are set, and
ChangesMade is fired, only if new value is different from existing value
Methods for RichTextBoxEx
- booleanvalue =
SaveFile([filename]) -- saves context of rich-text box to an RTF-format file. If filename is null or omitted, then a file dialog is invoked with
FilePath as the initial directory. Returns True if text was saved, False if dialog was canceled. Sets
IsTextChanged to False if successful.
- booleanvalue =
LoadFile([filename]) -- loads the contents of an RTF-format file into the rich-text box. If filename is null or omitted, then a file dialog is invoked with
FilePath as the initial directory. Returns True if text was loaded, False if dialog was canceled. Sets
IsTextChanged to False if successful.
- booleanvalue =
InsertPicture([filename]) -- inserts a picture into the rich-text box at the caret (in place of any selected text). If filename is null or omitted, then a file dialog is invoked with
FilePath as the initial directory. Returns True if picture was inserted, False if dialog was canceled. Sets Is
TextChanged to True, and fires
ChangesMade event, if successful.
Events for RichTextBoxEx
InsertRtfText -- allows host program to define custom (RTF) characters or functionality for various keystrokes.
e.KeyEventArgs -- information about keys being pressed (from "
e.RtfText -- custom text to be inserted (or replaced over selected text); format is RTF so that program can add functions and characters that are supported by the text box only through RTF codes (as opposed to properties and methods)
SmartRtfText -- allows host program to define smart (RTF) character-replacements for raw "dumb characters" as they are typed in.
e.KeyPressEventArgs -- information about keys being pressed (from "rtb"'s underlying
e.RtfText -- custom text to be take the place of the incoming character, and possibly immediately preceding characters before the caret; format is RTF so that program can add functions and characters that are supported by the text box only through RTF codes (as opposed to properties and methods);
e.PrecedingCharacterCount -- number of existing characters, precedding the incoming character, which are to be removed before adding smart character(s).
ChangesMade -- fires when changes are made to contents of rich-text box; it differs from
rtb_TextChanged in that it doesn't fire when "temporary" changes--like when the control does trial hyphenation in order to figure where hyphens can be inserted--take place.
IsTextChanged will always be True when this event fires.
Extension Methods for PrintRichTextBox
NOTE: These methods are designed work for a standard
RichTextBox, so that they work even if you're not using the
RichTextBoxEx control. (When using the
RichTextBoxEx control, use its
rtb property to reference the underlying
RichTextBox. Don't forget to
Import RichTextBoxEx or
Import RichTextBoxEx.PrintRichTextBox in any code file using the methods! When not using the UserControl, simply reference and
Import PrintRichTextBox to use with the standard control.)
Extension methods for printout:
richtextbox.Print(PrintDocument) -- prints text. The
PrinterSettings.PrintRange property of the
PrintDocument instance determines whether the current page (at the caret), pages with selected text, a range of pages, or the entire text is printed.
- dialogresult =
richtextbox.PrintPreview(PrintPreviewDialog) -- previews text. What's previewed depends, once again, on the
PrinterSettings.PrintRange property of the
PrintDocument assigned to the dialog.
- integerarray() =
richtextbox.PageIndexes(PrintDocument) -- returns an array with the start positions within the text box for each successive page. This can be used to determine what text would be printed on what page.
richtextBox.SetRightMarginToPageWidth(PageSettings) -- sets
richtextbox.RightMargin property so that text box wraps text at the same horizontal position as the printed page's width (within left and right margins). This method always sets control's
WordWrap property to False.
richtextbox.SetPageWidthToRightMargin(PageSettings) -- sets printed page's right margin so that it wraps text at the same horizontal position as indicated by
richtextbox.RightMargin property. This is SetRightMarginToPageWidth in reverse. If the
RightMargin property is 0, then no changes are made.
Extension methods for tracking scroll bars:
- scrollposition =
richtextbox.GetScrollPosition() -- gets current horizontal and vertical scroll-bar positions in pixels to a
richtextbox.SetScrollPosition(scrollposition) -- sets the horizontal and vertical scroll-bars to pixel positions specified in a
- scrollinfo =
richtextbox.GetScrollBarInfo(type[, mask]) -- returns the range, page-size, position, and/or track-position info about a scroll bar to a
PrintRichTextBox.ScrollInfo structure. type is a
PrintRichTextBox.ScrollBarType enum value specifying Horizontal or Vertical; mask is an optional bit-flag
PrintRichTextBox.ScrollBarMask enum value specifying what parameters to retrieve (by default, all 4 items).
- textwidth =
richtextbox.GetMaximumWidth() -- gets maximum horizontal width in pixels for any text. If control's
RightMargin property is non-zero, then that is used; otherwise, if
WordWrap is True, then control's client-area width is used; otherwise, the width of the widest line in the text is used. It also accounts for whether
ShowSelectionMargin is True or False.
Extension method for making lists (NEW!):
richtextbox.SetListStyle(liststyle) -- makes the selected text in the rich box a style specified; liststyle is a
PrintRichTextBox.RTBListStyle enum value specifying no list, bullets, Arabic numbers, lowercase letters, uppercase letters, lowercase Roman numerals, or uppercase Roman numerals for item headings. This method uses the rich-text box's
SelectionBullet property to turn on/off listing, then uses SendKeys to set the specific list type.
A few code examples are given below:
' using a property or method
RichTextBoxEx1.AllowSpellCheck = True : RichTextBoxEx1.UnitsForRuler = TextRuler.Inches
' using extension methods on underlying text box
Dim ScrollPosition As Point = RichTextBoxEx1.rtb.GetScrollPosition()
Dim Pages() As Integer = RichTextBoxEx1.rtb.PageIndexes(PrintDocument1)
RichTextBoxEx1.rtb.Select(Pages(2), Pages(3) - Pages(2)) ' select a page
RichTextBoxEX1.rtb.SetListStyle(RTBListStyle.LowercaseLetters) 'list as a, b, c, ...
' catching events
AddHandler RichTextBoxEx1_InsertRtfText, RichTextBoxEx1.InsertRtfText
AddHandler RTB_DoubleClick, RichTextBoxEx1.rtb.DoubleClick ' event for underlying text box
Shortcut keys are keystroke-defined similar to Word. For instance, Ctrl+X is Cut, Ctrl + I is Italic, Ctrl + F is Find, Ctrl + H is Replace, F3 is Find Next, F7 is Spell-Check, etc. Simply click on the control's constituent component, ContextMenuStrip1, to see them all. (At run time, the menus can, of course, be invoked by right-clicking the text box. If the ruler is shown, then the user can switch between inches and centimeters by right-clicking the ruler in order to show its menu,)
As for the pre-defined custom characters, here is the list of keystrokes:
Optional (syllable) hyphen
(- ; displays only when breaking
a word at the end of a line)
[Ctrl] + [-]
Em dash (—)
[Ctrl] + [Alt] + [-]
Left single quote (‘)
[Ctrl] + [`]
Left double quote (“)
[Ctrl] + [Shift] + [~]
Right single quote (’)
[Ctrl] + [']
Right double quote (”)
[Ctrl] + [Shift] + ["]
[Ctrl] + [Alt] + [C]
Registered trademark (®)
[Ctrl] + [Alt] + [R]
[Ctrl] + [Alt] + [T]
These custom-character-keystrokes can also be used for specifying special search and/or replacement text in the Find/Replace dialog; however, you can't customize their values, add additional custom characters, or specify "special actions" there like you can for the main control.
As for default smart-character conversions:
- A regular double quote ("), when typed, is changed to a left double quote (“) if tit occurs at the beginning of a line, or after a space, regular dash, em dash, tab, or left single quote; otherwise it is changed to a right double quote (”).
- A regular single quote ('), when typed, is changed to a left single quote (‘) if it occurs at the beginning of a line, or after a space, regular dash, em dash, tab, or left double quote; otherwise it is changed to a right single quote (’).
- A regular dash (-), when typed immediately after an existing regular dash is replaced (along with its preceding dash) with an em dash (—).
Features include the following:
- A Page Setup Dialog--invoked at the beginning of the program--to allow page size and margins to be set.
- Check boxes to enable/disable Spell-Check and Insert-Picture for the RichTextBoxEx control
- Displaying the UTF-32 Unicode values for the characters of the RichTextBoxEx's selected text in a (vertically-scrollable) standard TextBox--useful for determining which character codes do what in a RichTextBox--and showing the text box's current scroll position.
- Invoking the Print Dialog, followed by Print Preview and Print, when the text box is double-clicked or [Ctrl] + [P] is pressed.
- Saving or loading the text when [Ctrl] + [S] or [Ctrl] + [O], respectively, are pressed. Also, when exiting the application, the user is given the option to save text if it's been modified.
- The following custom characters (in addition to the default ones above):
[Ctrl] + 
[Ctrl] + 
[Ctrl] + 
Start new page (RTF code: \page ;
displays in text box only as carriage return,
but will affect printout of pages)
[Ctrl] + [Shift] + [Enter]
[Ctrl] + [S]
Load (open) text
[Ctrl] + [O]
It also converts "1/4", "1/2", "3/4"--when they are typed in--into "¼", "½", and "¾", respectively.
Points of Interest
- A word about the hyphenation and spell-checker tools: They regard all optional/syllable hyphens--visible and invisible alike--as word separators. Therefore, if text is already hyphenated, they may confuse part of a word for a whole word. One solution is to dehyphenate the selected text (using the context menu), do the spell-check, then re-hyphenate it (using the button or context menu). (Another is going to the "NetSpell" article with the spell-check utility and modifying it to handle hyphenated words [hyphen character: ChrW(173)]. It could also then be designed to "auto-hyphenate" known words.)
- The Find/Replace, Hyphenate, and Insert Symbol dialog boxes are implemented modally, so as to have greater control over any selected text and to simplify my coding. (Hit Cancel ([ESC]) to exit find or replace dialog, or to exit hyphenation early.) The scope of text subject to search/modification for these features--and Spell-check--is the whole text unless a region of text is highlighted, in which case only the selected text is worked with. Replace, Hyphenation, and Spell-check restore the highlighting if MaintainSelection is True; Find (and find/replace if the last operation is a simple find) does not. Invoking Find through Ctrl + F displays a "find only" dialog (no replacement options). Finally, Find Next (F3) is not limited in scope to highlighted text; it looks through the full text for a subsequent occurrence.
- There is ruler-bar for the control, the link to the article explaining it is above in the part about "dependencies".
- When using the Insert Symbol dialog, some character symbols occassionally force the selection of a special font for the character, and trigger an event cascade if one tries to subsequently change the font for text including the symbol. This is obviously a bug, and I will try to fix it in the next few days.