RichEdit Place Holder


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";

    SendMessage (hwndRE, EM_SETTEXTEX, (WPARAM)&settext, (LPARAM)wszPlaceholder);

To enable the place-holder functionality, send EM_SETEDITSTYLEEX to an empty control by


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.


Leave a comment

  • skSdnW

    “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”.

      • skSdnW

        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?

        • murraysMicrosoft employee

          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 ECM_FIRST is 0x1500. Also RichEdit has the TOM object model which has no counterpart in the common controls. Anyhow thanks for mentioning EM_SETCUEBANNER and we’ll get it into RichEdit. If you find other discrepancies, please mention them too!

          • skSdnW

            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.

          • murraysMicrosoft employee

            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. I’ll blog about some of these efforts in future posts.