Textboxes have been around since the invention of the GUI with the Xerox Star Workstation, and even before that in the form of text-based applications such as Turbo Pascal. Back in those days a textbox was essentially just text you enter within square brackets- eg:
1 2 |
Customer: [ ] Phone: [ ] |
Even so within this simple mechanic we found versatility. Some implementations used the size of the textbox as the maximum size; this was common for Database entry, where databases were commonly backed by an ISAM database or another similar database technology that either only worked with Fixed-Size records or worked best with fixed-size records. Others used them and allowed scrolling or even selection within the textbox- this was more common for standard User-input boxes such as entering filename specifications or other information.
Of course TextBoxes did change through the years, ever so slightly. In particular Windows® has an implementation of the TextBox control built into it’s standard API. This Control is known as the “EDIT” control, either after it’s inventor, Edward Itterby, or, more likely, given it was not invented by a person with that name, because it allows Text Editing. This control has evolved with Windows over the years, receiving many new capabilities and functionalities while still remaining compatible with applications using it.
One of these useful enhancements that we’ve received is “Cue Banners”. A Cue Banner is text that is displayed, in a grayed out fashion, in a TextBox when there is no text in it. This has become a very common Element that is found both on the Desktop and the Web; for Example, Outlook uses a Cue Banner for it’s Search Field:
As we can see here, the primary purpose of a Cue Banner is to indicate what a TextBox is for without using up the extra space that a Label would require. Even so a Cue Banner doesn’t necessarily replace a label, and is more useful for indicating what types of information can be entered. For example, extra functionality could be documented there- eg. ie Field Labelled “Customer” could specify via a Cue Banner “Enter Customer# or Account ID”.
We do, however, encounter a bit of an issue, in that the .NET framework doesn’t have very strong support for Cue Banners. Even WPF appears to linger behind when it comes to this. If you are interested in Cue Banners implemented in WPF, you would be much better served by This blog post; Which does better than I ever could.
That leaves us with Windows Forms. Which, given the title of this post, should surprise precisely nobody, except for Mr. Surprisio who is surprised by everything, including the mundane.
While Windows Forms could be considered ‘weaker’ when it comes to content management and layout than WPF, it also has the advantage that most of it’s controls are in fact Windows ‘core’ controls; that is, the Windows Forms TextBox is in fact a Windows Textbox, whereas a WPF TextBox is not. What this means for us is that while the WPF Cue Banner functionality requires a Content Adorner (as described in the excellent linked article above), for Windows Forms we can use the built-in support for Cue Banners in Windows itself.
P/Invoke
As a result of requiring access to some of the low level capabilities of the Windows Textbox not currently exposed by the Framework, we will need to use Platform Invoke. For obvious reasons there are some drawbacks to this approach, such as restricting the Operating System we can run on, since in this case it will be calling Windows APIs. In most circumstances this is not a big problem, but if you intend on targeting Mac OSX or Linux systems via the Mono Runtime you’ll have to think harder about how to implement the feature, because of course you cannot send Windows Messages on Linux or OSX.
In this particular case we want to send Window Messages to the TextBox (or ComboBox) control in question. Naturally- we do this with SendMessage. my typical approach for adding features like this to existing controls is to create extension methods, and to facilitate those I use private declarations for the DLL Imports. In this case, setting and retrieving the Cue Banner text via a message requires two different declarations and uses of SendMessage. to simplify development (and provide better static analysis) I define two static externs for SendMessage for each use, then define a standard static method that calls SendMessage for each accessor and then create extension methods that simply delegate to that static:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
using System; using System.Collections.Generic; using System.Data.Common; using System.Linq; using System.Runtime.InteropServices; using System.Text; public static class CueBannerExtensions { //we use P/Invoke to use the Cue Banner functionality present in the default Windows TEXTBOX class. //This is supported in XP and later. private const UInt32 ECM_FIRST = 0x1500; private const UInt32 EM_SETCUEBANNER = ECM_FIRST + 1; private const UInt32 EM_GETCUEBANNER = ECM_FIRST + 2; private const UInt32 CB_SETCUEBANNER = 0x1703; private const UInt32 CB_GETCUEBANNER = 0x1704; [DllImportAttribute("user32.dll", CharSet = CharSet.Unicode, EntryPoint = "SendMessage")] private static extern IntPtr SendMessageSetCueBanner([In()] IntPtr hWnd, [In()] UInt32 Msg, [In()] IntPtr wParam, [In()] StringBuilder lParam); [DllImportAttribute("user32.dll", CharSet = CharSet.Unicode, EntryPoint = "SendMessage")] private static extern IntPtr SendMessageGetCueBanner([In()] IntPtr hWnd, [In()] UInt32 Msg, [Out()] StringBuilder wParam, [In()] IntPtr lParam); public static bool IsCueBannerSupported() { //supported on XP and Later. var OSVersion = Environment.OSVersion; return OSVersion.Platform==PlatformID.Win32NT && OSVersion.Version >= new Version(5, 1, 0, 0); } /// <summary> /// Sets the Cue Banner on the TextBox. A Cue Banner is displayed in grayed text when there is no text entered. /// </summary> /// <param name="tb"/>TextBox to set the Cue Banner for. /// <param name="pCue"/>Cue Banner to set. /// <remarks>This method will call the Windows API SendMessage function, with the EM_SETCUEBANNER message.</remarks> public static void SetCueBanner(this System.Windows.Forms.TextBox tb, String pCue) { SetCueBanner(tb.Handle, pCue); } public static String GetCueBanner(this System.Windows.Forms.TextBox tb) { return GetCueBanner(tb.Handle); } public static String GetCueBanner(this System.Windows.Forms.ComboBox cb) { return GetCueBanner(cb.Handle,true); } public static void SetCueBanner(this System.Windows.Forms.ComboBox cb,String pCue) { SetCueBanner(cb.Handle,pCue,true); } public static void SetCueBanner(IntPtr Handle, String pCue,bool isCombo=false) { if (!IsCueBannerSupported()) return; SendMessageSetCueBanner(Handle, isCombo?CB_SETCUEBANNER:EM_SETCUEBANNER, new IntPtr(1), new StringBuilder(pCue)); } public static String GetCueBanner(IntPtr Handle,bool isCombo=false) { if (!IsCueBannerSupported()) return ""; //to retrieve, create a buffer and call SendMessage. we need a different P/Invoke signature to get output (which is why we have two SendMessage definitions). var text = new StringBuilder(256); var res = SendMessageGetCueBanner(Handle, isCombo?CB_GETCUEBANNER:EM_GETCUEBANNER, text, new IntPtr(text.Capacity)); if (res.ToInt64() != 0) { return text.ToString(); } return null; } } |
Easy as pie! Using it is absolutely trivial- though extension methods mean we cannot create properties, we can use the accessors and pretend they are properties. Just pretend you are working with Java:
1 2 |
textBox1.SetCueBanner("Testing Textbox"); comboBox1.SetCueBanner("Testing ComboBox"); |
Windows Forms is one thing, but it’s also older; when starting a new, whizbang project, WPF is the modern choice for a desktop application (Desktop Applications, being, themselves, somewhat passe). For WPF None of my solutions matched the simplicity found in A very nice project called WPF Bag-o-tricks
.
Have something to say about this post? Comment!