Click here to Skip to main content
15,879,535 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 249K   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

 
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 
GeneralRe: Thats wrong Pin
Romulus Corneanu1-Nov-10 3:35
Romulus Corneanu1-Nov-10 3:35 
GeneralRe: Thats wrong Pin
Xmen Real 2-Nov-10 15:12
professional Xmen Real 2-Nov-10 15:12 
GeneralVisual Studio 2003 Pin
ssaresky8-Apr-10 4:37
ssaresky8-Apr-10 4:37 
GeneralA small change. Pin
Romulus Corneanu15-Oct-08 4:24
Romulus Corneanu15-Oct-08 4:24 
GeneralWith only one character, watermark is still present Pin
dfjohn00518-Aug-08 23:12
dfjohn00518-Aug-08 23:12 
GeneralRe: With only one character, watermark is still present [modified] Pin
Stumproot18-Aug-08 23:49
Stumproot18-Aug-08 23:49 
GeneralRe: With only one character, watermark is still present Pin
Wael Alghool21-Aug-08 22:36
Wael Alghool21-Aug-08 22:36 
GeneralRe: With only one character, watermark is still present Pin
Stumproot22-Aug-08 0:08
Stumproot22-Aug-08 0:08 
GeneralRe: With only one character, watermark is still present Pin
Wael Alghool22-Aug-08 2:38
Wael Alghool22-Aug-08 2:38 
GeneralRe: With only one character, watermark is still present Pin
Stumproot22-Aug-08 3:01
Stumproot22-Aug-08 3:01 
GeneralRe: With only one character, watermark is still present Pin
Wael Alghool23-Aug-08 8:45
Wael Alghool23-Aug-08 8:45 
GeneralRe: With only one character, watermark is still present Pin
Stumproot24-Aug-08 4:54
Stumproot24-Aug-08 4:54 
GeneralRe: With only one character, watermark is still present Pin
Wael Alghool25-Aug-08 6:08
Wael Alghool25-Aug-08 6:08 
GeneralRe: With only one character, watermark is still present Pin
Stumproot25-Aug-08 10:21
Stumproot25-Aug-08 10:21 
GeneralRe: With only one character, watermark is still present Pin
Cobio4-Sep-08 13:19
Cobio4-Sep-08 13:19 
GeneralRe: With only one character, watermark is still present Pin
Wael Alghool23-Aug-08 8:59
Wael Alghool23-Aug-08 8:59 
GeneralRe: With only one character, watermark is still present Pin
Piyush K Patel19-Aug-08 2:04
professionalPiyush K Patel19-Aug-08 2:04 
GeneralFix for the constructor/timer problem Pin
lpgray23-Jul-08 12:04
lpgray23-Jul-08 12:04 
GeneralRe: Fix for the constructor/timer problem Pin
Stumproot18-Aug-08 23:47
Stumproot18-Aug-08 23:47 
GeneralNice idea Pin
Matthew Noonan21-Jul-08 16:40
Matthew Noonan21-Jul-08 16:40 

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.