Who sends the initial WM_UPDATEUISTATE message?


Last time, we looked at the confusingly-named WM_UPDATE­UI­STATE and WM_CHANGE­UI­STATE messages. But how does the whole indicator thingie get off the ground?

The default state for a window is to show all indicators. But as a special trick, the dialog manager will send a WM_UPDATE­UI­STATE message with UIS_INITIALIZE after the dialog has been initialized, which turns off the indicators if the last input event was a mouse event. This is its way of inferring whether the dialog box was triggered by a mouse or keyboard action and setting the initial indicators accordingly. (Note that if the user checked Underline keyboard shortcuts and access keys, then the dialog manager leaves the indicators enabled regardless of the last input event.)

That special WM_UPDATE­UI­STATE message is what gives dialog boxes the extra special feature of hiding the keyboard accelerators until you use the keyboard.

But notice that only the dialog manager does this. If you want this behavior in your own non-dialog windows, you will need to send the message yourself.

BOOL MyWindow::OnCreate(...)
{
 ... create and initialize any child windows ...

 // initialize indicators
 BOOL fAlwaysUnderline = FALSE;
 SystemParametersInfo(SPI_GETKEYBOARDCUES, 0,
                      &fAlwaysUnderline, 0);
 if (!fAlwaysUnderline) {
  SendMessage(this->m_hwnd, WM_UPDATEUISTATE,
              MAKEWPARAM(UIS_INITIALIZE, 0), 0);
 }
}

Exercise: Why is it important to create and initialize the child windows before sending the WM_UPDATE­UI­STATE message?

Exercise: Why can't the window manager do this automatically after WM_CREATE returns?

Exercise: Explain the behavior this customer observes.

We have a dialog box with three buttons. Sometimes the dialog displays underlines for the hotkeys, and sometimes it doesn't. I know about the feature which hides keyboard accelerators by default, but that doesn't explain why the setting gets ignored sometimes. The first time I show the dialog in my program, I get the underlines, but the second and subsequent times, I do not.

Comments (6)
  1. WndSks says:

    Why did this have to be special cased, why not just use UIS_SET?

    Does anyome know where UISF_ACTIVE is used and what it looks like?

    [You might want to change your user name. Generally speaking, telling somebody that they suck is not going to make them interested in interacting with you in a positive way. They're not going to bother checking whether you're being ironic. -Raymond]
  2. Dan Bugglin says:

    I'll take a crack.

    "Exercise: Why is it important to create and initialize the child windows before sending the WM_UPDATE­UI­STATE message?"

    So that the message can propagate to them. They can't receive it if they don't exist!

    Any accelerators are going to be displayed in child labels, menus, buttons…

    "Exercise: Why can't the window manager do this automatically after WM_CREATE returns?"

    I assume to allow the programmer to have control over it.  Also probably legacy software support.

    "Exercise: Explain the behavior this customer observes."

    Sounds like a case where the dialog is being hidden and reshown instead of destroyed, and this is affecting whether or not the UPDATEUISTATE message is sent?

    Honestly my GUI experience is mostly .NET, minimal with the C APIs.  So my guess here is really a stab in the dark.

    [Correct on #1. For #2, think about how dialog boxes are created. -Raymond]
  3. "Exercise: Explain the behavior this customer observes."

    The customer launches the program using keyboard (Enter on the icon, Ctrl+F9 on Visual Studio, etc.), so the last event is a keyboard one.

    [Yup, that was the reason. They were launching the program from the command prompt (therefore the last input was the Enter key). -Raymond]
  4. "Exercise: Why can't the window manager do this automatically after WM_CREATE returns?"

    Well, from the CREATESTRUCT documentation for lpCreateParams" member.

    "If the window is being created from a dialog template, this member is the address of a SHORT value that specifies the size, in bytes, of the window creation data. The value is immediately followed by the creation data."

    Since the CREATESTRUCT is only given to CreateWindow(Ex) and not stored anywhere else, the information that the window manager and anything else needs would be gone after the WM_CREATE message has finished processing.

    [Not sure how the CREATESTRUCT is involved. The question is "Why can't the window manager initialize the UI states when the window returns from its WM_CREATE handler?" -Raymond]
  5. I obviously over thought this. It has been a hard week and I was looking for problems where they didn't exist.

    Well, the dialog controls need to exist before it initialises the UI states, but from what I understand of the dialog creation procedure, it is:

    Get dialog template from somewhere (either resource, or via parameter)

    Create main dialog window using CreateWindowEx.

    Create child controls for dialog window using CreateWindowEx.

    Send WM_INITDIALOG message to the main dialog window.

    Show the window if the visible flag is set.

    For DialogBox only, run its own message pump.

    Since the WM_CREATE message is sent to a window before CreateWindowEx returns. By the time the WM_CREATE handler returns there are no controls at all. This means the window manager can't do it automatically after WM_CREATE finishes processing because the controls don't exist and the UI state initialisation requires the child controls to exist first.

    Hopefully I haven't overthought things this time.

  6. John Doe says:

    @Crescens2k, I think you got it right. The *Dialog* functions do quite a bit of work behind the scenes. One is to create the dialog, another is to create the controls. It sends a WM_INITDIALOG for initialization, since the WM_CREATEs were handled already, and then a WM_UPDATEUISTATE if the initialization was successful (note: the dialog procs are a bit different than the window procs, in the return argument and in not calling a default proc).

    Once you justify "Why is it important to create and initialize the child windows before sending the WM_UPDATEUISTATE message?", you can answer the second question with "Because is it important to create and initialize the child windows before sending the WM_UPDATEUISTATE message."

Comments are closed.