Patrick Smacchia [MVP C#]

Sponsors

The Lounge

Advertisement

Images in this post missing? We recently lost them in a site migration. We're working to restore these as you read this. Should you need an image in an emergency, please contact us at imagehelp@codebetter.com
Some RichTextBox tricks

I have recently been responsible for refactoring the Code Query Language query editor in NDepend to fix some imperfections.

 

 

 

The CQL query editor implementation is based on a class derived from the System.Windows.Controls.RichTextBox class. It was the opportunity to learn some tricks that I would like to share in the current post.

 

Text Coloring

If you google how to color the text displayed in a RichTextBox, you’ll certainly end up using the coloring selection trick, using the RichTextBox method Select() and properties SelectionColor, SelectionBackColor:

foreach (Highlight highlight in listOfHighlights) { this.Select(highlight.m_PosBegin, highlight.m_Length); this.SelectionColor = highlight.m_ForeColor; this.SelectionBackColor = highlight.m_BackColor; }

 
Using a search engine is very misleading here. We end up to the conclusion that this approach comes with extremely bad performance, even on short text with just dozens of word to color.

 

A much better way we found is to use the Rtf / Rich Text Format capabilities of the RichTextBox. You just need to format a rtf string and use this code:

this.Clear(); this.SelectedRtf = rtfString;

 

I won’t detail the Rtf format here. The Rtf string for the query above looks like:

{\rtf1\ansi\deff0{\fonttbl{\f0\fnil\fcharset0 Courier New;}} {\colortbl ;\red0\green128\blue0;\red255\green255\blue255;\red230\green255\blue230;\red0\green0\blue255;\red0\green0\blue0;\red0\green0\blue100;\red255\green255\blue153;}\fs20\cf1\highlight2 // <Name>\cf1\highlight3 Method refactored not 100% covered by tests\cf1\highlight2 </Name>\par \cf4\highlight2 SELECT\cf5\highlight2 \cf4\highlight2 METHODS\cf5\highlight2 \cf4\highlight2 WHERE\cf5\highlight2 \par \cf6\highlight2 PercentageCoverage\cf5\highlight2 \cf5\highlight2 <\cf5\highlight2 \cf5\highlight7 100\cf5\highlight2 \cf4\highlight2 AND\cf5\highlight2 \cf6\highlight2 CodeWasChanged\cf5\highlight2 \par \cf4\highlight2 ORDER\cf5\highlight2 \cf4\highlight2 BY\cf5\highlight2 \cf6\highlight2 PercentageCoverage\cf5\highlight2 \cf4\highlight2 DESC\cf5\highlight2 \cf5\highlight2 ,\cf5\highlight2 \par \cf6\highlight2 NbLinesOfCodeCovered\cf5\highlight2 \cf5\highlight2 ,\cf5\highlight2 \cf6\highlight2 NbLinesOfCodeNotCovered\cf5\highlight2 \par \par \cf1\highlight2 // To run this constraint properly 2 analysis must be compared.\par \cf1\highlight2 // This can be done in throught the menu: \par \cf1\highlight2 // Start Page -> Compare 2 versions of a code base\par \cf5\highlight2 \par \cf1\highlight2 // To run this constraint properly coverage data must be \par \cf1\highlight2 // gathered from NCover™ or Visual Studio™ Coverage.\par \cf1\highlight2 // More information on how to import coverage data here:\par \cf1\highlight2 // \cf1\highlight2 http://www.ndepend.com/Coverage.aspx\cf1\highlight2 \par

 

Notice that using the SelectedRtf property is also a good way to prevent improper formatted text copy/pasted from Microsoft Word or a Browse for example.

 

Avoid flickering problem

When you update the content of your RichTextBox, you’ll certainly notice some pesky flickering. Hopefully, an efficient solution to this problem can be found here. Basically the solution consists in disabling text redrawing by calling some win32 APIs:

private const int WM_SETREDRAW = 0x000B; private const int WM_USER = 0x400; private const int EM_GETEVENTMASK = (WM_USER + 59); private const int EM_SETEVENTMASK = (WM_USER + 69); [DllImport("user32", CharSet = CharSet.Auto)] private extern static IntPtr SendMessage(IntPtr hWnd, int msg, int wParam, IntPtr lParam); IntPtr eventMask = IntPtr.Zero; try { // Stop redrawing: SendMessage(richTextBox1.Handle, WM_SETREDRAW, 0, IntPtr.Zero); // Stop sending of events: eventMask = SendMessage(richTextBox1.Handle, EM_GETEVENTMASK, 0, IntPtr.Zero); // change colors and stuff in the RichTextBox } finally { // turn on events SendMessage(richTextBox1.Handle, EM_SETEVENTMASK, 0, eventMask); // turn on redrawing SendMessage(richTextBox1.Handle, WM_SETREDRAW, 1, IntPtr.Zero); }

 

Testing for ScrollBars’ visibility

After looking for a way to test if the RichTextBox’s ScrollBars are visible or not, the only way I found is to infer this information from the delta between this.ClientRectangle and this.Size. This is certainly not the cleanest way but it is working well in every context I tried:

// Determine scrollbar visibility by comparing this.ClientRectangle and this.Size. private bool HScrollVisible { get { Rectangle clientRectangle = this.ClientRectangle; Size size = this.Size; return (size.Height - clientRectangle.Height) >= SystemInformation.HorizontalScrollBarHeight; } } private bool VScrollVisible { get { Rectangle clientRectangle = this.ClientRectangle; Size size = this.Size; return (size.Width - clientRectangle.Width) >= SystemInformation.VerticalScrollBarWidth; } }

 

Get/Set the ScrollBars’ positions

To achieve this I came to the conclusion that it must be done throught the good-old win32. Being able to get and set the ScrollBars’ positions is especially useful to avoid some pesky automatic RichTextBox content re-locating I notice in some circumstances, such as inserting or modifying a long text. Here is the code:

private const int SB_HORZ = 0x0; private const int SB_VERT = 0x1; private const int WM_HSCROLL = 0x114; private const int WM_VSCROLL = 0x115; private const int SB_THUMBPOSITION = 4; [DllImport("user32.dll", CharSet=CharSet.Auto)] private static extern int GetScrollPos(int hWnd, int nBar); [DllImport("user32.dll")] private static extern int SetScrollPos(IntPtr hWnd, int nBar, int nPos, bool bRedraw); [DllImport("user32.dll")] private static extern bool PostMessageA(IntPtr hWnd, int nBar, int wParam, int lParam); internal int HScrollPos { private get { return GetScrollPos((int)this.Handle, SB_HORZ); } set { SetScrollPos((IntPtr)this.Handle, SB_HORZ, value, true); PostMessageA((IntPtr)this.Handle, WM_HSCROLL, SB_THUMBPOSITION + 0x10000 * value, 0); } } internal int VScrollPos { private get { return GetScrollPos((int)this.Handle, SB_VERT); } set { SetScrollPos((IntPtr)this.Handle, SB_VERT, value, true); PostMessageA((IntPtr)this.Handle, WM_VSCROLL, SB_THUMBPOSITION + 0x10000 * value, 0); } }

 

Url Detection

Something that I wasn’t aware: if you want to display Urls that can be clicked in your text box, just set the RichTextBox.DetectUrls property to true. You can then use the RichTextBox.LinkClicked event to handle the url click.

 

 


Posted Mon, Jul 7 2008 6:55 PM by Patrick Smacchia

[Advertisement]

Comments

Yann Trevin wrote re: Some RichTextBox tricks
on Mon, Jul 7 2008 3:00 PM

Thanks for the tips. I have been already disapointed in the past by the limitations of the RichTextBox control support in winform, although the capabilities of the control itself are amazing And I am generally relunctant on using P/Invoke on non hardware or driver related stuff. But except by using a web container control, there is indeed not too many other choices. Thank you for sharing the info.

Reflective Perspective - Chris Alcock » The Morning Brew #131 wrote Reflective Perspective - Chris Alcock &raquo; The Morning Brew #131
on Tue, Jul 8 2008 3:18 AM

Pingback from  Reflective Perspective - Chris Alcock  &raquo; The Morning Brew #131

2 Static » Blog Archive » Some RichTextBox tricks wrote 2 Static &raquo; Blog Archive &raquo; Some RichTextBox tricks
on Tue, Jul 8 2008 3:37 AM

Pingback from  2 Static  &raquo; Blog Archive   &raquo; Some RichTextBox tricks

Dew Drop - July 8, 2008 | Alvin Ashcraft's Morning Dew wrote Dew Drop - July 8, 2008 | Alvin Ashcraft's Morning Dew
on Tue, Jul 8 2008 7:33 AM

Pingback from  Dew Drop - July 8, 2008 | Alvin Ashcraft's Morning Dew

make money online wrote make money online
on Tue, Jul 8 2008 7:52 AM

Thanks for the great post! Looking forward to many more. B>)

Chris Pietschmann wrote VB.NET: Syntax Highlighting in a RichTextBox control
on Tue, Jul 8 2008 6:50 PM

VB.NET: Syntax Highlighting in a RichTextBox control

Gowdhaman wrote re: Some RichTextBox tricks
on Tue, Jan 13 2009 4:24 AM

Good Collection... Resource of Rich Text Box

Patrick Smacchia [MVP C#] wrote My 100th blog post: Top 5 development practices you should care for
on Wed, Mar 25 2009 3:49 AM

This is a modest number but I am happy to have reached it, especially taking account that I spend weekly

Community Blogs wrote My 100th blog post: Top 5 development practices you should care for
on Wed, Mar 25 2009 3:59 AM

This is a modest number but I am happy to have reached it, especially taking account that I spend weekly

Alejo wrote re: Some RichTextBox tricks
on Thu, Apr 9 2009 6:30 PM

Wich is the order of position of this Code in a operation of coloring?

Blake Robertson wrote re: Some RichTextBox tricks
on Tue, May 12 2009 4:58 PM

Just wanted to say thanks.  I used your VScrollVisible trick in a AutoScalingRichTextBox control which has methods to make the font as large as possible in the given bounds of the control.  

I linked to this page here:  

stackoverflow.com/.../autoscale-font-in-a-textbox-control-so-that-its-as-big-as-possible-and-still-fits

Kabar wrote re: Some RichTextBox tricks
on Sat, Dec 12 2009 5:47 PM

Just thanks! Very usefull.

Add a Comment

(required)  
(optional)
(required)  
Remember Me?