为啥我没有收到 WM_UPDATEUISTATE?

Posted

技术标签:

【中文标题】为啥我没有收到 WM_UPDATEUISTATE?【英文标题】:Why am I not getting WM_UPDATEUISTATE?为什么我没有收到 WM_UPDATEUISTATE? 【发布时间】:2017-03-06 23:15:15 【问题描述】:

我有一个带有编辑控件和自定义按钮的主窗口。当编辑控件获得焦点并且我按下 Tab 键时,按钮控件获得焦点但它从未收到 WM_UPDATEUISTATE?我在主窗口的消息循环中使用IsDialogMessage()。关于为什么会发生这种情况的任何想法?

编辑:为什么自定义按钮控件没有收到WM_UPDATEUISTATE

编辑:IsDialogMessage() 在此示例中未发送 WM_UPDATEUISTATEWM_CHANGUISTATE

#include <windows.h>
#include <tchar.h>

HINSTANCE g_hInst;

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
 
    static HWND hBtn1, hBtn2;

    switch(msg)
    
    case WM_CREATE:
        hBtn1 = CreateWindowEx(0, TEXT("Button"), TEXT("Button 1"), WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
            4, 4, 100, 40, hwnd, 0, g_hInst, 0);
        if(!hBtn1) return -1;

        hBtn2 = CreateWindowEx(0, TEXT("Button"), TEXT("Button 2"), WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
            114, 4, 100, 40, hwnd, 0, g_hInst, 0);
        if(!hBtn1) return -1;
        break;

    case WM_DESTROY:
        PostQuitMessage(0);
        break; 

    default:
        return DefWindowProc(hwnd, msg, wParam, lParam);
    

    return 0;



int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
 
    const TCHAR szClassName[] = TEXT("Main////");
    WNDCLASSEX wc;
    HWND hwnd;
    MSG msg;

    SecureZeroMemory(&wc, sizeof(WNDCLASSEX));
    wc.cbSize        = sizeof(WNDCLASSEX);
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wc.hCursor       = LoadCursor(0, IDC_ARROW);
    wc.hIcon         = LoadIcon(0, IDI_APPLICATION);;
    wc.hInstance     = hInstance;
    wc.lpfnWndProc   = WndProc;
    wc.lpszClassName = szClassName;
    if(!RegisterClassEx(&wc)) return 0;

    g_hInst = hInstance;
    hwnd = CreateWindowEx(0, szClassName, TEXT("Main"), WS_OVERLAPPEDWINDOW, 140, 140, 440, 240, 0, 0, hInstance, 0); 
    if(!hwnd) return 0;

    ShowWindow(hwnd, nCmdShow);

    while(GetMessage(&msg, 0, 0, 0) > 0)
    
        if(!IsDialogMessage(hwnd, &msg))
        
            TranslateMessage(&msg); 
            DispatchMessage(&msg);
        
    

    return (int)msg.wParam;

当按下 Tab 键时,我在主窗口和 2 个按钮上使用了 spy++,但我在任何地方都看不到 WM_UPDATEUISTATEWM_CHANGEUISTATE,但标准按钮控件绘制了焦点矩形。标准按钮如何知道何时绘制焦点矩形?

【问题讨论】:

您是否将WM_CHANGEUISTATE 消息传递给默认窗口过程,以便它可以传播到父窗口,父窗口又将WM_UPDATEUISTATE 发送到其子窗口。 如果您的意思是当按钮获得焦点时,那么不,我不是。但是我怎么知道按钮是否因为按下了 Tab 键而获得焦点? 是的,默认窗口程序正在处理WM_CHANGEUISTATE IsDialogMessage() 发送WM_CHANGEUISTATE,需要传递给默认窗口过程。默认窗口过程将消息传播到它的父窗口,直到它找到一个没有父窗口的窗口。该窗口的默认窗口过程然后将此消息转换为WM_UPDATEUISTATE 消息,该消息将传递给所有子窗口。您可以为任何窗口调用IsDialogMessage(),而不仅仅是对话框。 minimal reproducible example 可能会有所帮助。 【参考方案1】:

当一个窗口获得焦点时,它应该显示它的焦点指示器。为此,控件应注意WM_SETFOCUS 和WM_KILLFOCUS。当它获得焦点时,行为良好的控件将通过向自身发送WM_QUERYUISTATE 来检查 UI 状态,如果 UI 状态设置了 UISF_HIDEFOCUS 标志,则该控件不会绘制焦点指示器。

WM_CHANGEUISTATE 和WM_UPDATEUISTATE 负责控制窗口树(包括控件)是否应该显示键盘“提示”和焦点指示器一般。例如,如果提示未显示并且您点击 ALT 键,这些消息将更新树中窗口的 UI 状态,以便它们开始显示提示。这些消息不用于从一个控件隐藏焦点指示器并在下一个控件上显示为用户通过控件的 TAB。之所以会出现这种效果,是因为控件会注意焦点更改消息。

一般来说,自定义控件应该注意焦点变化消息并让 DefWindowProc 处理 UI 状态消息。

Raymond Chen 有一系列关于 UI 状态以及这些消息如何传播的博文。

Untangling the confusingly-named WM_UPDATEUISTATE and WM_CHANGEUISTATE messages Who sends the initial WM_UPDATEUISTATE message? Why does tapping the Alt key cause my owner-draw static control to repaint? What is the documentation for SetParent trying to tell me about synchronizing the UI state? Demonstrating what happens when a parent and child window have different UI states Getting a parent and child window to have the same UI states

当我最近尝试再次解决所有这些问题时,我发现这些很有帮助。关于这个主题的官方文档的当前状态有些模糊和模棱两可。

注意:“助记符”、“加速器”和“热键”这些术语已经混淆在一起,即使在 Microsoft 官方文档和一些 API 名称中也是如此。当我说“键盘提示”时,我主要指的是助记符(有时称为“快捷方式”或“访问键”)。这些是控件标签和菜单项中的下划线字符,让用户知道ALT+&lt;character&gt; 将选择带标签的项。

【讨论】:

以上是关于为啥我没有收到 WM_UPDATEUISTATE?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我没有收到任何加速度计更新?

为啥我收到错误“类型‘PublicTableViewController’没有成员‘handleRefresh’’

为啥我收到远程通知时没有发送本地通知?

为啥我没有收到“跨线程操作无效”错误

为啥我的 iPhone 没有收到任何通知?

Qt:为啥我的按钮没有收到信号?