Numbering of lines in text editors is a well known feature. But the standard
RichTextBox 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.
RichTextBox is not a standard control in Windows Forms. It does not use the
OnPaint 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 of
RichTextBox, there is a satisfactory solution that does not use pure Win32 API functions.
We implement the
RichTextBox with numbered lines as a
UserControl. We will not override anything in the
RichTextBox, we will only use its events. Our
NumberedTextBoxUC consists of
Label is used for displaying the line number and
RichTextBox for the text content, both are contained in
The content of
numberLabel is updated in the
RichTextBox's event handlers. These events are:
There are several problems with this implementation. The first one is scrolling. Unlike the VS source code editor or
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 large
Labels, you cannot afford to print too many lines to
Label in each
OnTextChanged event handler. Another strange problem is the
RichTextBox in .NET 2.0 uses strange line indentation, which is impossible to turn off or set to zero. The same font in
RichTextBox results in different line positions when the controls are top aligned.
I am displaying only the numbers of visible lines, thus the unnecessary hidden line numbers are not printed. The update function is called
updateNumberLabel(). It uses the
GetLineFromCharIndex to determine the first and last visible line numbers.
private void updateNumberLabel()
Point pos = new Point(0, 0);
int firstIndex = richTextBox1.GetCharIndexFromPosition(pos);
int firstLine = richTextBox1.GetLineFromCharIndex(firstIndex);
pos.X = ClientRectangle.Width;
pos.Y = ClientRectangle.Height;
int lastIndex = richTextBox1.GetCharIndexFromPosition(pos);
int lastLine = richTextBox1.GetLineFromCharIndex(lastIndex);
pos = richTextBox1.GetPositionFromCharIndex(lastIndex);
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 the
Label 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.
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 small
numberLabel location update in each
OnVScroll event handler. The
Label is moved about as many pixels different from multiples of the
RichTextBox 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 with
RichTextBox functions in this way results in text shivering.
private void richTextBox1_VScroll(object sender, EventArgs e)
int d = richTextBox1.GetPositionFromCharIndex(0).Y %
(richTextBox1.Font.Height + 1);
numberLabel.Location = new Point(0, d);
I hope this user control helps developers handling
RichTextBox. I appreciate your advice and improvements to this control.