Community Pick: Many members of our community have endorsed this article.

TCTextBox – A Homemade Textbox supports transparent background

This article introduced a TextBox that supports transparent background.

 TCTextBox demo


TextBox is the most widely used control component in GUI design. Most GUI controls do not support transparent background and more or less do not have the urge for it. TextBox is among one of them, but unfortunately, once in a while people want it to be transparent (such as this typical question in EE: Transparent TextBox in C#).

If you search on the internet for a solution, you will come across advices like subclassing TextBox and add the support to transparent background color, or, drawing your own background in the OnPaint event. It is rational theoretically but unfeasible practically, as revealed in the following analysis.

(For those who like fast food, you can skip the literature sections and jump to the “Using the Code” section to grab and eat. Actually I also get my hands wet immediately after a short hunting with no luck. After a couple of days working, when I decide to present this home-made component to public, I have to head back to the anatomization part to make the article complete. It costs me much more time than doing the simply coding stuff along.)

The traditional TextBox just refused to be tweaked

To verify the above common suggestions, I build a phantom class TestTextBox derived from TextBox. Again, test cases are flagged in the source code for users who like to practise by themselves.

Set background color as Color.transparent (Test Case 1)

The visual designer does not allow you to set the transparent color to the original TextBox. After subclassing it, you can enable it by adding:

this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);

Open in new window

When you run the program, you would be glad to see the TextBox showing the background image on the form. The image will stay there if you do not touch it. Once you start typing, the background returns back to normal color along with the text. When you refresh it, the TextBox becomes transparent again but the text is “gone”.

Redraw background in OnPaint (Test Case 2)

The original TextBox does not even open the OnPaint event for users to subscribe it. After you subclass it, you’d better override the event handlers rather than to subscribe the events (even when it’s available). We leave out the part on how to compose an image, simply draw a pseudo image in OnPaint.

protected override void OnPaint(PaintEventArgs e)
                                  if (backgroundImage == null)
                                      backgroundImage = this.Parent.BackgroundImage; //Pretended that you have captured the background as image.
                                  if (backgroundImage != null)
                                      #region Test Case 2: 
                                      e.Graphics.DrawImage(backgroundImage, 0,0);   //Uncomment to view the effect.

Open in new window

Don’t forget to add another SetStyle to trigger the OnPaint event:

SetStyle(ControlStyles.UserPaint, true);

Open in new window

When you run the program, you can see the image is drawn in the TextBox. But again, you cannot get the image and text shown at the same time.

Hack the WM_PAINT  message in WndProc (Test Case 3)

We can play a lot of magic by hacking the message loop, but not this one. We put here basically the same code as those in OnPaint. The runtime performance is even worse. If you do not add a “return” after drawing the image, the base TextBox will always draw itself on top of whatever you have done. The background image will never get a chance to show up on surface. If you add the “return” to block the WM_PAINT message passing on to base, the text will never appear.

All in all, the build-in control is competing with user-drawn image on the stage.

Bob Bradley’s AlphaBlendTextBox

In the late 1990s, I was once working on an automation software for a kiosk project. It would monitor all the processes and applications running in the system. Sometimes it needed to send a mouse click or keyboard input to a certain control within a windows interface to perform actions in behalf of user, so that the user would not be confused by the system messages and focus on the internet surfing. I noticed that many GUI components actually contain more handles than they appear to be, what is so called “comprised control”. It is difficult to dissemble them apart.

So the solution of AlphaBlendTextBox is actually using a PictureBox fully covering up the TextBox. Flooding all the keyboard and mouse to the underlying TextBox, so that the TextBox could be working as normal in the behind, then the PictureBox displays an image containing the appearance of the TextBox with Alpha blending. It’s a really smart solution and not quite so arduous as described by himself. I might simply use his diea if I had spent more time on reading his code in the beginning. So far, with my limited search on the internet resources, I guess the AlphaBlendTextBox is the only one that succeeded through the drawing approach. I may do some optimization on his code later on – if there are demands.

My Homemade TCTextBox

When developing my tiny drawing tool TCPaint, I want to support in-place text input so that users can see what they will get while typing. Since both transparent and opaque background is supported for other parts of the drawing, I would like text editing support both as well. So I write my own text box.

It is derived directly from Control:

class TCTextBox : Control
                              public TCTextBox()
                                  this.SetStyle(ControlStyles.UserPaint, true);
                                  this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
                                  this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
                                  this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
                                  this.Cursor = System.Windows.Forms.Cursors.IBeam;
                                  this.ImeMode = System.Windows.Forms.ImeMode.On;

Open in new window

The first three SetStyle is to eliminate the possible flickering effect while redrawing itself. The forth SetStyle is to enable the transparent background. Then the cursor is set to IBeam as a visual indication that you can type in this control. And finally turn on the ImeMode to support multi-language input.

Now you have a transparent control looks like you can type in it. But if you are really typing, nothing will show up in the box, because the control does not process any key strike yet. There are a bunch of methods related to keyboard input:

protected override void OnPreviewKeyDown(PreviewKeyDownEventArgs e)
protected override void OnKeyDown(KeyEventArgs e)
protected override void OnKeyUp(KeyEventArgs e)
protected override void OnKeyPress(KeyPressEventArgs e)
protected override bool IsInputKey(Keys keyData)
protected override bool IsInputChar(char charCode)
protected override bool PreProcessMessage(ref Message msg)
protected override bool ProcessKeyEventArgs(ref Message m)
protected override bool ProcessDialogKey(Keys keyData)
protected override bool ProcessDialogChar(char charCode)

They are invented for different purposes. You can read MSDN for details. ProcessDialogChar is the proper one to intercept char input and the only one to enable multiple language IME tool. (Here is a nice guidance if you do not know what is multiple language input:How to Set Up & Use Multiple Language Input Options on Your Computer)

protected override bool ProcessDialogChar(char charCode)
                      base.Text += charCode;
                      return true;

Open in new window

Then display the input in OnPaint:

protected override void OnPaint(PaintEventArgs e)
                                  e.Graphics.DrawString(Text, Font, new SolidBrush(this.ForeColor), 
                                      new RectangleF(0, 0, this.Width, this.Height), StringFormat.GenericDefault);

Open in new window

Now you have a control in which you can type something and it will display what you have hit. Writing a textbox is not so terrifying at all:)

You can subclass and override the above methods from any kind of control. Then the control will be enabled to accept text input and display it. Of course you need to think about why you would do that.

I would not bore you out by the technical details. The TCTextBox currently supports:

Caret: I use Win32 functions to display a caret. You can draw a customized one if you like.
Caret position: It will go up/down, left/right when an arrow key is pressed.
AutoScroll: If the text is longer than the height of the display window, it will automatically scroll up or down to put the current position in the viewport.
Selection: You can select any length of text using mouse click and drag. The three properties: SelectionStart, SelectionLength, SelectionText, are supported.
Copy & Paste: You can either use short cut keys or context menu to interact with the system clipboard.
Background image: You can set an image in a TCTextBox as background. You can of course set your own BackColor, ForeColor, Font, Size to it. They are supported natively by the base class already.

That’s all. The remaining features of the original TextBox have not been implemented. And the supported features have not been fully tested. Use it at your own risk.

Using the Code

After adding the source file TCText.cs into your project, you should be able to see a new component in toolbox. Then you can select and use it as normal controls in Visual Designer.

The source code and a demo project can be downloaded from here:

And the simple drawing tool demonstrates the in-place editing:     TCPaint.exe

Comments (3)

Did you really develop this function or you just copied from another link and add some ideas?
Sorry for that, but I've seen this already, maybe your the one's in the link author.


systan, would you mind post the link where you see the same TXTextBox? Did you compare the source code bwtween the two?

It's also normal that not only one person think about the needs and create something similar:)

You aren't Disposing your 'SolidBrush' object.

You should wrap the new solidbrush in a using.

Have a question about something in this article? You can receive help directly from the article author. Sign up for a free trial to get started.