Click here to Skip to main content
15,878,852 members
Articles / Programming Languages / C#
Article

WaterMark TextBox For Desktop Applications Using C#, .NET 3.5 and VS2008

Rate me:
Please Sign up or sign in to vote.
4.54/5 (35 votes)
22 Aug 2008CPOL3 min read 248.9K   9.8K   74   54
Watermark TextBox for .NET
WaterMarkTextBox.png

Introduction

During my work in a (CMS) Content Management System, and in the module of publishing (which is a .NET desktop application) I needed a watermark feature to help users to know the functionality of a certain TextBox.

WaterMark is a gray text in a TextBox that must disappears once the user starts to input his text, and must again appear if the user erases all of his text characters.

Background

I discovered that TextBox doesn't support this nice feature (am I wrong!?) Whatever, I enjoyed sub classing TextBox to implement a watermark feature myself, it reminded me of the past/nice days of VC++.

It didn't take more than 8 hours to subclass the TextBox control (.NET 3.5) and then obtain a good feature like watermark. I Googled three web-pages or more in order to know how to enable and disable the overriding of the OnPaint handler, where, OnPaint is ignored by default until modifying the control style UserPaint, which can be modified by using the following statement:

C#
this.SetStyle(ControlStyles.UserPaint, true);

And to render WaterMarkText on the client area of the TextBox control it was easy to use PaintEventArgs.DrawString(...), because it works just as easily as setting the TextBox.Text property. Take a look:

C#
args.Graphics.DrawString((WaterMarkTextEnabled ? WaterMarkText : Text),
drawFont, drawBrush, new PointF(0.0F, 0.0F));

I was also in need of subscribing some events to toggle WaterMark On and Off according to the state of the TextBox.Text property. It may be empty (which means WaterMark-enabled) or full of characters (means WaterMark-disabled). This includes events like:

C#
this.TextChanged +=
new System.EventHandler(this.WaterMark_Toggel);

this.LostFocus
+= new System.EventHandler(this.WaterMark_Toggel);

Careful — while TextBox is in the state of "WaterMark-enabled", the TextBox.Font property seems to be garbage collected after calling this.SetStyle(...). This seems to occur when using the Brush property for example, (you find this by trial and error), so, I was obliged to save TextBox.Font into a oldFont member variable until WaterMark goes to a "disable" state, then returns back to its origin.

TextBox.Font may itself be changed for various reasons (i.e., a user may decide to change the font), so, I was again obliged to subscribe the TextBox.FontChanged event to update oldFont so that at the correct time of modification, subscription is done by using the following statement:

C#
this.FontChanged
+= new System.EventHandler(this.WaterMark_FontChanged);

At that time, the joined events will not be capable to change the state of TextBox-control/WaterMark-feature, so, the constructor must initialize the WaterMark-toggling process along with joining these events at the time of construction. But, the control isn't yet constructed/created totally, where some members like TextBox.Font are not created, and we will not be able to catch oldFont. So, we must stay waiting for the control for awhile until it is created totally and then call WaterMark_Toggel. To solve that problem I used OnCreateControl handler that makes it possible to trigger WaterMark_Toggel at the end of the creation process (thanks is to 'lpgray' who tolled me about OnCreateControl).

Again, WaterMark toggling is being done by calling WaterMark_Toggel(null,null) which depends on TextBox.Text to decide which state should be assigned to the control (WaterMark-enable or WaterMark-disable). Take a look at the JoinEvents(true) member function.

Using the Code

This code was written on Visual Studio 2008, .NET 3.5 and C#.

To use the code just take a copy from the file WaterMarkTextBox.cs or from the following code snippet, and place it in your project, then, build the project, you will get a component icon on your ToolBox named WaterMarkTextBox. Drag the icon to your form and enjoy WaterMarkText and WaterMarkColor properties.

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing;

namespace wmgCMS
{
    class WaterMarkTextBox : TextBox
    {
        private Font oldFont = null;
        private Boolean waterMarkTextEnabled = false;

        #region Attributes 
            private Color _waterMarkColor = Color.Gray;
            public Color WaterMarkColor
            {
                get { return _waterMarkColor; }
                set { _waterMarkColor = value; Invalidate();/*thanks to Bernhard Elbl
                                                              for Invalidate()*/ }
            }

            private string _waterMarkText = "Water Mark";
            public string WaterMarkText
            {
                get { return _waterMarkText; }
                set { _waterMarkText = value; Invalidate(); }
            }
        #endregion

        //Default constructor
        public WaterMarkTextBox()
        {
            JoinEvents(true);
        }

        //Override OnCreateControl ... thanks to  "lpgray .. codeproject guy"
        protected override void OnCreateControl() 
        { 
            base.OnCreateControl();
            WaterMark_Toggel(null, null); 
        }
        
        //Override OnPaint
        protected override void OnPaint(PaintEventArgs args)
        {
            // Use the same font that was defined in base class
            System.Drawing.Font drawFont = new System.Drawing.Font(Font.FontFamily,
                Font.Size, Font.Style, Font.Unit);
            //Create new brush with gray color or 
            SolidBrush drawBrush = new SolidBrush(WaterMarkColor);//use Water mark color
            //Draw Text or WaterMark
            args.Graphics.DrawString((waterMarkTextEnabled ? WaterMarkText : Text),
                drawFont, drawBrush, new PointF(0.0F, 0.0F));
            base.OnPaint(args);
        }

        private void JoinEvents(Boolean join)
        {
            if (join)
            {
                this.TextChanged += new System.EventHandler(this.WaterMark_Toggel);
                this.LostFocus += new System.EventHandler(this.WaterMark_Toggel);
                this.FontChanged += new System.EventHandler(this.WaterMark_FontChanged);
                //No one of the above events will start immeddiatlly 
                //TextBox control still in constructing, so,
                //Font object (for example) couldn't be catched from within
                //WaterMark_Toggle
                //So, call WaterMark_Toggel through OnCreateControl after TextBox
                //is totally created
                //No doupt, it will be only one time call
                
                //Old solution uses Timer.Tick event to check Create property
            }
        }

        private void WaterMark_Toggel(object sender, EventArgs args )
        {
            if (this.Text.Length <= 0)
                EnableWaterMark();
            else
                DisbaleWaterMark();
        }

        private void EnableWaterMark()
        {
            //Save current font until returning the UserPaint style to false (NOTE:
            //It is a try and error advice)
            oldFont = new System.Drawing.Font(Font.FontFamily, Font.Size, Font.Style,
               Font.Unit);
            //Enable OnPaint event handler
            this.SetStyle(ControlStyles.UserPaint, true);
            this.waterMarkTextEnabled = true;
            //Triger OnPaint immediatly
            Refresh();
        }

        private void DisbaleWaterMark()
        {
            //Disbale OnPaint event handler
            this.waterMarkTextEnabled = false;
            this.SetStyle(ControlStyles.UserPaint, false);
            //Return back oldFont if existed
            if(oldFont != null)
                this.Font = new System.Drawing.Font(oldFont.FontFamily, oldFont.Size,
                    oldFont.Style, oldFont.Unit);
        }

        private void WaterMark_FontChanged(object sender, EventArgs args)
        {
            if (waterMarkTextEnabled)
            {
                oldFont = new System.Drawing.Font(Font.FontFamily,Font.Size,Font.Style,
                    Font.Unit);
                Refresh();
            }
        }
    }
}

Points of Interest

Subclassing .NET controls.

History

  • 15/6/2008: Posted version 1.0.
  • 9/8/2008: Posted version 1.5. - Use 'OnCreateCotrol' instead of ' Timer.Tick', use 'Invalidate' instead of 'Refresh' and remove unwanted property 'WaterMarkTextEnabled'
  • 22/8/2008: Posted version 1.6. - Update demo application to show how to use WatermarkColor and Font properly.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect Government
Qatar Qatar
Programmer since 1990 with Pascal, VC++, C#, ASP.NET, jQuery, J2EE and Android.
PMP Certified since 2009.
PSP Certified since 2005.
Business & System analyst since 2004.
Led teams in between 8 to 30 members.
Worked for www.beinsports.net, www.harf.com, www.islamweb.net, islam.gov.qa, islamonline.net.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Franc Morales22-Sep-22 18:53
Franc Morales22-Sep-22 18:53 
SuggestionFont not updating (fixed) Pin
Member 1323992221-Aug-22 0:28
Member 1323992221-Aug-22 0:28 
SuggestionWaterMark Alignment as the TextAlign Propertie Pin
catanaion12-Jul-18 22:17
catanaion12-Jul-18 22:17 
BugWatermark on MultiLine TextControl Pin
catanaion11-Jul-18 20:22
catanaion11-Jul-18 20:22 
BugWatermark Cursor/Font while changing the base Font of the TextControl Pin
catanaion11-Jul-18 20:19
catanaion11-Jul-18 20:19 
GeneralRe: Watermark Cursor/Font while changing the base Font of the TextControl Pin
Franc Morales22-Sep-22 18:46
Franc Morales22-Sep-22 18:46 
SuggestionFor Center Alignment Pin
Boony Developer7-Aug-17 6:24
Boony Developer7-Aug-17 6:24 
QuestionKryptonTextbox Pin
Cool Smith17-Nov-16 21:35
Cool Smith17-Nov-16 21:35 
QuestionCan we apply RightToLeft property on WaterMarkText? Pin
Member 1227268630-Sep-16 2:21
Member 1227268630-Sep-16 2:21 
QuestionGreat Article Pin
Member 1165127712-Feb-16 6:01
Member 1165127712-Feb-16 6:01 
Questionhow to increase the height of this water mark Pin
sher_azam27-May-14 1:54
sher_azam27-May-14 1:54 
Questionwater mark text box Fixed Single Borderstyle Pin
Member 1029340818-Apr-14 0:13
professionalMember 1029340818-Apr-14 0:13 
SuggestionRe: water mark text box Fixed Single Borderstyle Pin
catanaion12-Jul-18 22:25
catanaion12-Jul-18 22:25 
SuggestionThanks! I would recommend... Pin
BigFish20002-Sep-13 19:46
BigFish20002-Sep-13 19:46 
GeneralRe: Thanks! I would recommend... Pin
User 751107820-Oct-13 4:47
professionalUser 751107820-Oct-13 4:47 
GeneralRe: Thanks! I would recommend... Pin
Nazareno Manco7-Oct-15 3:27
Nazareno Manco7-Oct-15 3:27 
QuestionAdd click event Pin
Member 217319024-Aug-13 6:53
Member 217319024-Aug-13 6:53 
Clicking the control while mouse is over, will not redraw the watermark when releasing mouse button.
To fix add:
Click += ToggleWatermark;
DoubleClick += ToggleWatermark;

Also the behaviour does not take into account right to left languages. You should implement it to get the StringFormat while drawing.

My class: http://pastebin.com/7tQ1f5TZ[^]
The StringFormat extension: http://pastebin.com/vMh9Tv53[^]

modified 24-Aug-13 13:03pm.

QuestionMore Readable Pin
NehruKandasamy4-Mar-13 18:01
NehruKandasamy4-Mar-13 18:01 
AnswerRe: More Readable Pin
Wael Alghool6-Mar-13 17:40
Wael Alghool6-Mar-13 17:40 
GeneralMy vote of 5 Pin
Renju Vinod6-Feb-13 19:27
professionalRenju Vinod6-Feb-13 19:27 
SuggestionPlease update the code section. Pin
Malli_S24-Jan-12 19:49
Malli_S24-Jan-12 19:49 
GeneralMy 4. Pin
Malli_S24-Jan-12 19:47
Malli_S24-Jan-12 19:47 
GeneralMy vote of 2 Pin
Sharad Kulkarni13-Oct-10 19:07
Sharad Kulkarni13-Oct-10 19:07 
GeneralMy vote of 3 Pin
sunilmcahnbgu12-Sep-10 20:14
sunilmcahnbgu12-Sep-10 20:14 
GeneralThats wrong Pin
Xmen Real 20-Jun-10 15:01
professional Xmen Real 20-Jun-10 15:01 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.