Watermarking Edit Controls Using C# or Visual Basic

WEBINAR: On-demand webcast

How to Boost Database Development Productivity on Linux, Docker, and Kubernetes with Microsoft SQL Server 2017 REGISTER >

Sometimes, it is the little things that can end up being quite annoying. This is especially if these little tasks become tedious and you have to repeat them a lot of times. Any developer has such a task. Any developer has that one thing that frustrates him or her, or a task that they simply hate doing. (It sounds as if I am in a bad mood. No; I am not.)

For me, the task that I hate the most is clearing Textboxes after they have been populated, and setting the Textboxes' default text. This usually involves creating a Sub procedure (or two) to clear all input fields on a form when, say, for example: a button has been clicked; and then setting its default text back to a descriptive prompt, such as: "Please enter name."

Granted, if you do not have many input fields and forms, this isn't really bothersome, but it may get boring after a few hundred forms on a few hundred projects. Yes, I know there may be better ways to structure these little tasks, but hey, time is not always on your side.

Today, I will show you a wonderful trick that I never knew existed until a few months ago. This trick will prevent you from having to manually clear and re-populate all your text fields on your form (as described above). You will create a project in which you create a Class Library that makes use of a few Windows APIs to add a watermark feature to all your text controls. This watermark will be placed in your text controls automatically, and once the text control receives the focus, the watermark will automatically disappear. This saves a lot of tedious work.

Our Projects

I have decided to include both C# and VB.NET code in this article.

Open Visual Studio and create either a C# Windows Forms project or a Visual Basic.NET Windows Forms project. After the project has been created, design your Form to resemble Figure 1.

Design
Figure 1: Design

You will return to your Windows Forms Project later. Add a new project to your Windows Forms Project (again, either C# or VB.NET) by clicking File, Add, New Project. In the New Project dialog box, select Class Library, as shown in Figure 2

Class Library
Figure 2: Class Library

You may name your Class Library anything you desire. I have named mine C_Watermark and VB_EditWatermark.

Code

Add the following namespaces to your Class Library.

C#

using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;

VB.NET

Imports System.Runtime.InteropServices
Imports System.Windows.Forms

These namespaces are necessary to enable us to communicate with Windows Forms objects (such as our textboxes and comboboxes) as well as to import the necessary Windows APIs into our project.

Add the Windows APIs.

C#

   [DllImport("user32.dll", CharSet = CharSet.Auto)]
   private extern static Int32 SendMessage(
   IntPtr hWnd,
   int msg,
   int wParam, [MarshalAs(UnmanagedType.LPWStr)]
   string lParam);

   [DllImport("user32",
      EntryPoint = "FindWindowExA",
      ExactSpelling = true,
      CharSet = CharSet.Ansi, SetLastError = true)]

   private static extern IntPtr FindWindowEx(IntPtr hWnd1,
      IntPtr hWnd2, string lpsz1, string lpsz2);
   private const int EM_SETCUEBANNER = 0x1501;

VB.NET

   <DllImport("user32.dll", CharSet:=CharSet.Auto)>
   Private Function FindWindowEx(
      ByVal hWnd1 As IntPtr,
      ByVal hWnd2 As IntPtr,
      ByVal lpsz1 As String,
      ByVal lpsz2 As String) As IntPtr

   End Function
   Private Function SendMessage(
      ByVal hWnd As IntPtr,
      ByVal msg As Integer,
      ByVal wParam As Integer, <MarshalAs(UnmanagedType.LPWStr)>
      ByVal lParam As String) As Int32
   End Function

   Private Const EM_SETCUEBANNER As Integer = &H1501]

The FindWindowEx API function retrieves a handle to a window whose class name and window name match the supplied parameters. The SendMessage API sends the given message (in this case, the EM_SETCUEBANNER message) to a window.

Add the SetWatermark procedure.

C#

   public static void SetWatermark(this Control ctl, string text)
   {

      if (ctl is ComboBox)
      {
         IntPtr Edit_hWnd = FindWindowEx(ctl.Handle, IntPtr.Zero,
            "Edit", null);
         if (!(Edit_hWnd == IntPtr.Zero))
         {
            SendMessage(Edit_hWnd, EM_SETCUEBANNER, 0, text);
         }
      }
      else if (ctl is TextBox)
      {
         SendMessage(ctl.Handle, EM_SETCUEBANNER, 0, text);
      }
   }

VB.NET

   <System.Diagnostics.DebuggerStepThrough()>
   <System.Runtime.CompilerServices.Extension()>
   Public Sub SetWatermark(ByVal ctl As Control, _
         ByVal text As String)

      If TypeOf ctl Is ComboBox Then

         Dim Edit_hWnd As IntPtr = FindWindowEx(ctl.Handle, _
            IntPtr.Zero, "Edit", Nothing)

         If Not Edit_hWnd = IntPtr.Zero Then

            SendMessage(Edit_hWnd, EM_SETCUEBANNER, 0, text)

         End If

      ElseIf TypeOf ctl Is TextBox Then

         SendMessage(ctl.Handle, EM_SETCUEBANNER, 0, text)

      End If
   End Sub

The SetWatermark procedure determines the type of control that is being passed to it. If it is either a ComboBox or a TextBox set the watermark by using the supplied text.

Build your Class Library project. After the build, there should be no errors; you need to add a Reference to this Class Library in your Windows Forms projects. Do this by clicking Project, Add Reference…, Solution, and ticking the box next to your Class Library name (see Figure 3).

Add Reference to Library
Figure 3: Add Reference to Library

Add the Class Library namespace to your Form's code.

C#

re>using C_Watermark;

VB.NET

Imports VB_EditWatermark

This simply links the Form to the Library.

In the Form's Load event, type the following:

txtFirstName.Set

You will notice whilst typing that Visual Studio's AutoComplete shows SetWatermark as an added textbox Property, as shown in Figure 4.

Property
Figure 4: Property

Add the rest of the code:

C#

   txtFirstName.SetSetWatermark("Enter Name");
   cboTitle.SetWatermark("Choose Title");

VB.NET

   txtFirstName.SetWatermark("Enter Name")
   cboTitle.SetWatermark("Choose Title")

Run your application.

You will notice that the prompts only show when the box doesn't have the focus and disappear when the box has the focus.

Running
Figure 5: Running

The C# source code and VB.NET source code are available on GitHub.

Conclusion

The little things matter, not only on the end user's screen, but in the coding world. This feature has saved me tons of hours and put me in a better mood.



About the Author

Hannes DuPreez

Hannes du Preez has been a Microsoft MVP for Visual Basic from 2008 to 2017. He loves technology and loves Visual Basic. He loves writing articles and proving that Visual Basic is more powerful than what most believe. You are most welcome to reach him at: ojdupreez1978@gmail.com

Related Articles

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • As all sorts of data becomes available for storage, analysis and retrieval - so called 'Big Data' - there are potentially huge benefits, but equally huge challenges...
  • The agile organization needs knowledge to act on, quickly and effectively. Though many organizations are clamouring for "Big Data", not nearly as many know what to do with it...
  • Cloud-based integration solutions can be confusing. Adding to the confusion are the multiple ways IT departments can deliver such integration...

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date