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.
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
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).
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.
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.
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.