
Introduction
Numbering of lines in text editors is a well known feature. But the standardRichTextBox
in .NET 2.0 does not support this feature. It is also hard to find a suitable solution on the Internet. Especially, a solution that does not directly use Win32 functions.
TheRichTextBox
is not a standard control in Windows Forms. It does not use theOnPaint
method and other similar functions as it should and it also hides some of the important properties required for proper customization. One way to overcome this is to use win32 API functions and override the WndProc functions. I don't like this way of doing it but I am forced to use it again and again. I consider it as a fault of .NET developers.
Fortunately, for numbering lines ofRichTextBox
, there is a satisfactory solution that does not use pure Win32 API functions.
Implementation
We implement theRichTextBox
with numbered lines as aUserControl
. We will not override anything in theRichTextBox
, we will only use its events. OurUserControl
namedNumberedTextBoxUC
consists ofSplitContainer
,Label
(numberLabel
) andRichTextBox
.Label
is used for displaying the line number andRichTextBox
for the text content, both are contained inSplitterContainer
.
The content ofnumberLabel
is updated in theRichTextBox
's event handlers. These events are:
OnTextChanged
OnVScroll
OnSizeChanged
OnFontChanged
Problems
There are several problems with this implementation. The first one is scrolling. Unlike the VS source code editor orTextBox
control,RichTextBox
uses smooth scrolling, thus scrolling with the scrollbar scrolls the text in pixels, not in lines. You will notice that the first line is displayed in half. This is not always a wanted behaviour and I would appreciate the possibility to turn it off. Another problem is the redrawing speed of largeLabel
s, you cannot afford to print too many lines toLabel
in eachOnTextChanged
event handler. Another strange problem is theRichTextBox
in .NET 2.0 uses strange line indentation, which is impossible to turn off or set to zero. The same font inLabel
andRichTextBox
results in different line positions when the controls are top aligned.
Solution
I am displaying only the numbers of visible lines, thus the unnecessary hidden line numbers are not printed. The update function is calledupdateNumberLabel()
. It uses theRichTextBox
functionsGetCharIndexFromPosition
andGetLineFromCharIndex
to determine the first and last visible line numbers.
privatevoid updateNumberLabel(){//we get index of first visible char and//number of first visible line Point pos =new Point(0,0);int firstIndex = richTextBox1.GetCharIndexFromPosition(pos);int firstLine = richTextBox1.GetLineFromCharIndex(firstIndex);//now we get index of last visible char//and number of last visible line pos.X = ClientRectangle.Width; pos.Y = ClientRectangle.Height;int lastIndex = richTextBox1.GetCharIndexFromPosition(pos);int lastLine = richTextBox1.GetLineFromCharIndex(lastIndex);//this is point position of last visible char, we'll//use its Y value for calculating numberLabel size pos = richTextBox1.GetPositionFromCharIndex(lastIndex);//finally, renumber label numberLabel.Text ="";for (int i = firstLine; i <= lastLine +1; i++) { numberLabel.Text += i +1 +"\n"; }}
For different line indentations I have found a constant, which works best for font size 8. The size of theLabel
font is bigger for this constant. I hope, in future versions of .NET this issue will be fixed and both the fonts will be exactly the same, as it was in .NET 1.1.
public NumberedTextBoxUC(){ InitializeComponent(); numberLabel.Font =new Font(richTextBox1.Font.FontFamily, richTextBox1.Font.Size +1.019f);}
Smooth scrolling is another issue which causes a lot of troubles. I use the smallnumberLabel
location update in eachOnVScroll
event handler. TheLabel
is moved about as many pixels different from multiples of theRichTextBox
font height, thus the modulo text position of the font height. The reverse solution, to update the text position to be line aligned, is in my opinion impossible without using Win32 functions. Updating the text position withRichTextBox
functions in this way results in text shivering.
privatevoid richTextBox1_VScroll(object sender, EventArgs e){//move location of numberLabel for amount//of pixels caused by scrollbarint d = richTextBox1.GetPositionFromCharIndex(0).Y % (richTextBox1.Font.Height +1); numberLabel.Location =new Point(0, d); updateNumberLabel();}
Conclusion
I hope this user control helps developers handlingRichTextBox
. I appreciate your advice and improvements to this control.