1

I thought I'd take a stab at my own simple TextBox Control. I'm not quite sure how they're written, but I took a look at the Reference Source for the Windows Forms TextBox, and it's just added a whole lot more questions so I thought I would ask my own questions which are based on how I think a TextBox Control is made:

How are characters/text drawn? Since I'll be creating the TextBox from scratch, I'll obviously need to draw. So knowing that when you draw something in WinForms, you can't just select that text, you need to handle the MouseDown and MouseMove events, get the Location where the mouse is being pressed down, and then determine which, if any, character is at that location. But we can't really do that unless we've saved that character somewhere along with its coordinates. Which means we'll probably need to use a list to store everything that the user types:

List<Character> characters = new List<Character>();

class Character
{
    public string Text { get; set; }
    public Location { get; set; }
    public Size { get; set; }
}

Now that we've got the location of that character, we'll need to draw a filled rectangle so the user knows what they're selecting. We can do that by getting the Size and Location of the character at the coordinates we've determined previously.

Is this basically how a TextBox works?

1) When user types something, we use DrawString to draw what was "typed" and then store its Size and Location in a List for future reference? 2) When the user "selects" text, we lookup the coordinates the user "selected" in the List and then draw a filled rectangle at that Location?

6
  • 3
    You probably would want to store the text as a String rather than individual characters, and use Graphics.DrawString(...) to do the rendering. The reason is for international characters individually appear different than when combined in a word. That does bring up an interesting question on how to determine the correct caret position. Commented Sep 9, 2014 at 5:05
  • @Loathing that's a really good point, and thanks for mentioning it. Commented Sep 9, 2014 at 5:08
  • 5
    Creating a TextBox from scratch is a really large order to take on. It is possible but to make it work well will be rather hard. The most important word in your question is 'simple' as my own simple TextBox Control.. - So you should make a written list of all the functionality you want to implement. Commented Sep 9, 2014 at 18:40
  • The textbox inherits from TextBoxBase, so even textbox.cs is a not a from-scratch project. And it uses TextRenderer for output. Commented Oct 28, 2014 at 20:19
  • 2
    A text box has lots of additional functionality that needs to be handled to get correct behavior. For example, it shows a blinking caret, it can wrap text, it can support right-to-left rendering with Unicode fonts, it can handle various keyboard shortcuts, has different behavior on mouse interaction (single, double, triple click, drag/drop, highlight, change caret position), etc. Commented Oct 29, 2014 at 16:59

1 Answer 1

6
+150

I did write my own WinForms TextBox (in order to satisfy a need for spell checking during data entry, and highlighting of incorrect spellings via font underlines and colours). During my investigation for this I found that .net didn't carry out the text box rendering itself (it is a Windows facility) so had to invent it all from scratch.

My approach was very similar to yours but the important point to remember is that no one data structure is sufficient to store everything about a text box. Specifically a text box should have collections for the following;

  1. The list of characters.
  2. TextRuns which are indexes into that list of characters for all characters which share the same presentation - i.e. Font, Color etc. There will typically be fewer TextRuns than Characters.
  3. One Highlight TextRun to record the characters currently highlighted by the user
  4. Carat Position - this can be tricky. A textbox carat can be before the first character or after the last character, or any character in between.
  5. An Image of the last painted textbox - like a buffer, so that OnPaint messages can be quickly satisified without repeated MeasureString and DrawString calls. This can be typically larger than the renderable area of the TextBox in the case of multi-line or scrollable boxes, in which case you render a Rectangle subset on request.
  6. A list of RenderRuns which represent the position and size of a set of characters, all of which are in the same TextRun, and are painted on the same text box line, and is the basic unit of painting. A RenderRun will have offset position within the Image and a Size which enables you to select the correct RenderRun for any given position in the Image. Subsequent selection of a specific character would require a MeasureString for each character in the RenderRun. you can choose to store the character sizing in the RenderRun or dynamically calculate it each time you need it.

A consequence of all this was

  • The custom text box was notably slower than the Windows native equivalent
  • It took a lot more memory

So we only used it on those fields where spell checking was required.

Ultimately I would advise against attempting it, because although it is possible the overall "feel" was that it wasn't a native Windows textbox and it behaved very subtly differently (not least of which in the way it scrolled - by pixel rather than by line).

Hope this helps.

Sign up to request clarification or add additional context in comments.

2 Comments

Thank you very much @Phillip, this is really helpful. I'm still going to attempt it (otherwise I won't be able to stop thinking about it), but I don't expect it to be as good as native anyway. The fact that I had to ask this question is proof enough to me that I won't be getting myself a native experience anytime soon :)
A good idea would be to download the Open XML Document Library from here msdn.microsoft.com/en-us/library/office/…. I realise this gives you the data structures for a Word Document, but a "rich" Text Box will need exactly the same data structures, and this might be a really good way of basing your design on an existing structure which does the same kind of job, plus its fully documented. I'm not suggesting you implement a WordProcessor but use the lib as a guide to what kinds of things you need to store and process.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.