Sometimes you need a text box that cues the user to type something in, such as, “Start a conversation”. As soon as the user types something, the cue text vanishes, and the user sees what the user typed. If the user deletes all the text, the cue text reappears. Such a text box is called a place-holder control. The Microsoft 365 RichEdit has such a control. This post explains how to include it in your application.
Send two messages to set up a place-holder control: 1) EM_SETTEXTEX to set the place-holder text, and 2) EM_SETEDITSTYLEEX to enable the place-holder functionality. For setting the text, write something like
const WCHAR wszPlaceholder [] = L"Start a conversation"; SETTEXTEX settext = {ST_PLACEHOLDERTEXT, 1200}; SendMessage (hwndRE, EM_SETTEXTEX, (WPARAM)&settext, (LPARAM)wszPlaceholder);
To enable the place-holder functionality, send EM_SETEDITSTYLEEX to an empty control by
SendMessage (hwndRE, EM_SETEDITSTYLEEX, SES_EX_SHOWPLACEHOLDERONFOCUS, SES_EX_SHOWPLACEHOLDERONFOCUS);
At this point, the place-holder text “Start a conversation” is displayed. The constants ST_PLACEHOLDERTEXT and SES_EX_SHOWPLACEHOLDERONFOCUS aren’t documented on the web, so here they are
#define SES_EX_SHOWPLACEHOLDERONFOCUS 0x80000000 #define ST_PLACEHOLDERTEXT 0x0010
Internally, the place-holder functionality is implemented using two stories, one for the main text and one for the place-holder text. A story is a programming object that stores rich text. More about this kind of story in another post. Whenever the main text is empty, the place-holder story is displayed on screen. As soon as the user types something, the main story is displayed. The initial place-holder facility was added in 2005, but SES_EX_SHOWPLACEHOLDERONFOCUS functionality was added in 2018 and isn’t in the Windows msftedit.dll. The XAML TextBox uses RichEdit and supports place-holders.
“Such a text box is called a place-holder control” Naming things is hard. The plain edit box uses EM_SETCUEBANNER and the listview calls it “empty text” AND “empty markup”.
Thanks! I didn’t know about EM_SETCUEBANNER. I’ll add a work item to implement it in RichEdit and update this post when the item is completed.
This is a bit surprising to me since that message is probably 20 years old by now. Why is there so little synchronization between the RichEdit team and the common controls team? Do you even try to avoid stomping on each other when adding new messages?
Good question. I wasn't involved in the place-holder design except in suggesting that we use a scratch story for it. The request for the facility came from within Office where RichEdit is used widely. The RichEdit message IDs are in a different range from the messages of the common controls, so conflicts don't occur. RichEdit-specific messages start at WM_USER (0x0400) and currently end at WM_USER + 391, while the EM_SETCUEBANNER is ECM_FIRST + 1, where...
In the RichEdit 2.0 days (Office '97), we tried to emulate the system edit control's messages and the SES_EMULATESYSEDIT edit style helps. Since RichEdit is a rich-text editor as well as a plain-text editor, there may be some discrepancies. Given the long histories of the system edit control and RichEdit, we try to avoid changing current functionality since it might break existing applications. We do add functionality regularly to support new clients with special requirements....
ECM_FIRST is somewhat “new”. In the very early days RichEdit and plain edit used a lot of the same messages and compatibility between them was pretty good, probably 90+%. Did the RichEdit team just implement the messages that existed for Edit in Windows 95 (except the handle feature) and then never looked back?
Compatibility between the two is very handy when you end up having to switch to a RichEdit for better font support.