Introduction

Sure anyone can make a line numbering user control. I've made many variations of them for VB 3.0 to 6.0 & C++, always using picture boxes or whatever as a drawing base to mark the position. When Microsoft released the newer versions of RichText control boxes (in more recent versions), the old methods went out the door. By this, I mean that one can easily zoom in & out of the box by holding down Ctrl (or Shift) key while using the mouse wheel, posing a HUGE problem for traditional methods.

Before Compiling Fore-note

I have successfully compiled & ran this example project under both Visual Studio 2008 & SharpDevelop 3.x using Framework 2.0.

You will probably have problems running this example project under VS 2005 & lower; do note the control does work under the Framework 2.0. If you are having problems running this under VS 2005 or other studios, you might want to either find yourself a project converter or upgrade your studio. I am very sorry if this is an inconvenience for you, that is why this is posted for Intermediate & Expert users.

This will not show under Custom Components unless you build you the project first!

Under Custom Components, insert "LineNumbers_For_RichTextBox" into your form as well as insert a RichTextBox

Line them up side by side (left side by default)

Link Line Numbers to your RichTextBox

Under "Add LineNumbers to" > "Parent Rich Text Box":

Select your RichTextBox to add line numbers to

(feel free to customize it on your own)

Run the project!

Background and Points of Interest

As a software designer/developer like all of you, time is of the essence when meeting deadlines! This example using the RichText control works great! Especially with the zoom in/out feature using the mouse wheel, and it added nice graphical features which can be easily customized. However, I feel the urging need to program in C# and this example was only provided in VB.NET... It was time to upgrade it & now, on with the show!

Unfortunately that is a sideeffect of using the RichTextBox. This type of user control is resource intensive when the files get VERY large. For instance, when trying to load a 20 MB log file you will run into problems.

If you have a small project, this type of user control is great. Otherwise might want to look into Scintilla for C# or another custom control which may be able to resolve the large file issue.

When i add the files and rebuild i get this:Error 1 Program 'C:\Users\{username}\Documents\Visual Studio 2008\Projects\c#\luaediter\Lua Editer\obj\Release\Lua Editer.exe' does not contain a static 'Main' method suitable for an entry point

Go for it! The only thing i ask is a small line of some sort of, "thank you" in the credits.

Please let me know of the product that it is being used in (even if the code was re-vamped and changed). This way I can post Success Stories/Projects. In return this can possibly help generate more traffic or recognition for you.

I noticed that the line numbers seemed to disappear when I displayed an RTB with hundreds of lines. ??

I looked in Update_SizeAndPosition() and found that the edge of the linenumbers control was being moved outwards, either left or right, depending on how it was docked to the parent RTB. When there were, say, 4 digits to display, and the linenumbers control was displayed on the left, the left edge of it would move further leftward. This became a problem when the control was anchored to the left edge of the form.

So I reworked the Update_SizeAndPosition() method, based on the principle that the bounding box of the linenumebers control plus its parent should not change, regardless of how wide the digits display could become. In other words, when docked to the left of the RTB, the left edge of the linenumbers control should never move further leftward, and the right edge of the RTB should never move further rightward.

Conversely, when docked to the right edge of the RTB, the right edge of the linenumbers control should nevevr move further rightward, and the left edge of the RTB should never move further leftward. If that makes sense.

The only movement, then, should be on the internal borderline, between the two controls. As the linenumbers control needs to expand, the RTB should shrink, horizontally.

The modified code looks like this:

elseif (zDockSide != LineNumberDockSide.None)
{
// --- DockSide is active L/R/H
// line up the tops of the numbers and the RTB
zNewLocation.Y = zParent.Top;
zNewSize.Height = zParent.Height;
// Principle: keep the overall size of the RTB and
// the RtbLineNumbers, the same.
// --------------------------------------------
// The left edge of the RtbLineNumbers must remain
// fixed when it is displayed to the left. The
// right edge of the RtbLineNumbers when it is
// displayed to the right.
// --------------------------------------------
//// Corollary: only move the border between the two.
if (zAutoSizing_Size.Width > 0)
zNewSize.Width = zAutoSizing_Size.Width;
int delta = zNewSize.Width - this.Size.Width;
if (delta == 0) return; // no change!
var newParentSize = zParent.Size;
newParentSize.Width = newParentSize.Width - delta;
if (zDockSide == LineNumberDockSide.Left)
{
// The line numbers appear to the left of the RTB.
// For the left edge to remain fixed,
// this.Location.X must remain constant.
// Move the left border of the RTB and resize it,
// as necessary.
var newParentLoc = zParent.Location;
newParentLoc.X = zNewLocation.X + zNewSize.Width + 1;
zParent.Location = newParentLoc;
}
elseif (zDockSide == LineNumberDockSide.Right)
{
// The line numbers appear to the right of the RTB.
// For the right edge to remain fixed,
// this.Location.X + this.Width must remain constant
// Move the right border of the RTB as necessary (hence,
// resize it).
zNewLocation.X = this.Location.X - delta;
}
zParent.Size = newParentSize;
this.Location = zNewLocation;
this.Size = zNewSize;
}

Damian, after looking at this further, I thought to try a different tack.
Rather than defining a companion control, as you did here, I extended RichTextBox. This RichTextBoxEx displays line numbers itself, within its bounds, in response to the Paint event. Some pictures.

(replacing c < by c <=) - I have not tested this exhaustively, but I didn't face any problems with this solution so far.

2. If the line-numbers increase the number of digits (e.g. from 9 to 10), the size of the line-number-space is not calculated correctly. If you add another line (e.g. from 10 to 11), it works as expected.

Did you check the requirements over all of the requirements? The most common mistake made by most is, You must compile your program after adding the file & the reference. After that, the control will be visible and able to be used.

One side note, this was never tested under Express Edition. If you do not have the funds or means to have a full version of Visual Studio (2005/2008/2010) feel free to try, SharpDevelop at www.icsharpcode.net It is a completely free, full packed alternative to MS Visual Studio.

that was heavily looked into.. ran a debug on a few of the recursive functions & it does call itself a lot! The issue lies when trying to get the sum & individual height of each line in the file. If this was a fixed height box like most code boxes, this issue wouldn't arise.

I am always open to suggestions! Maybe if we had the RTF box set as a fix-font, this would help lower the impact of this issue.

I looked into this as well. Found that the TextChanged() method gets called very very often. I had coupled this with a RTB that was displaying XML, and also formatting the XML with syntax highlighting. With every format change, the zParent_Changed method was being invoked, which then invalidated the control and caused a repaint.

That didn't work out so well. Definitely need a way to distinguish between types of changes and be more intelligent about re-painting. It's possible you could use a backoff timer. If you get 72 zParent_Changed() calls in a row, wait.... until they stop coming. And only do the refresh/invalidate after a short delay when no further zParent_Change events occur. Haven't tested this, it's only an idea.

The other thing: inside Update_VisibleLineNumberItems(), there are a number of things that can be easily optimized. For one thing, every retrieval of zParent.Text is expensive. So, retrieve it once at the top of the method and replace all zParent.Text with a reference to the local copy. Like this:

if (zParent == null)
return;
string txt = zParent.Text; // getting the text is expensive. We do it once.
if (string.IsNullOrEmpty(txt))
return;
...

For another thing, the timer in that method is sort of odd. It's very simple to replace it with a stopwatch. Not sure if that's a performance thing, but it makes the code cleaner.

I used this Control and it is very good. But I still have problems with displaying numbers of lines with big text file. I made changes described in previous answer without counting zParent_Changed().
I have a text file with more then 1000 of lines, 50-100 characters in line.
What can I do to adjust performance? Can this counting zParent_Changed() help this much?