如何检测子窗口上的 WM_KEYDOWN?
Posted
技术标签:
【中文标题】如何检测子窗口上的 WM_KEYDOWN?【英文标题】:How do I detect a WM_KEYDOWN on child window? 【发布时间】:2021-07-23 11:04:54 【问题描述】:对于像按钮、编辑等标准控件,我可以将其子类化,但是对于子窗口(我用作容器)如何完成?我知道WM_KEYDOWN
已发送到父窗口,但我无法直接从相应的子窗口获取它。我试过子类化它(不确定这是否有意义,因为它是一个窗口并且在WNDCLASSW.lpfnWndProc
成员中已经有自己的窗口过程)但是这个子过程无论如何都没有收到WM_KEYDOWN
消息。我可以通过使用GetCursorPos()
和WindowFromPoint()
从光标位置(我也需要考虑键盘焦点)获取控件的hwnd,如下所示,但我觉得它很hacky。这样做的正确方法是什么?
目前看起来是这样的:
case WM_KEYUP:
case WM_KEYDOWN:
POINT p;
if(GetCursorPos(&p))
HWND control = WindowFromPoint(p);
if(control)
// just testing it
int len = GetWindowTextLength(control);
wchar_t buffer[len + 1];
GetWindowText(control, buffer, sizeof(buffer));
MessageBox(NULL, buffer, L"", MB_OK);
break;
窗口如下所示:
目标是在用户键入内容的相应窗口上捕获 WM_KEYDOWN,然后调用一个函数来处理此事件,例如window1_onKeyDown()
、window2_onKeyDown()
等。
这里是完整的代码:
#pragma comment(lib, "user32.lib")
#pragma comment(lib, "Comctl32.lib")
#pragma comment(lib, "Gdi32.lib")
#define WIN32_LEAN_AND_MEAN
#define UNICODE
#define _UNICODE
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK WndProc1(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK WndProc2(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
void createWindow1(HWND);
void createWindow2(HWND);
HBRUSH hBrush1, hBrush2;
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PWSTR lpCmdLine, int nCmdShow)
MSG msg;
WNDCLASSW wc = 0;
wc.lpszClassName = L"my window";
wc.hInstance = hInstance;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpfnWndProc = WndProc;
wc.hCursor = LoadCursor(0, IDC_ARROW);
hBrush1 = CreateSolidBrush(RGB(173, 164, 237));
hBrush2 = CreateSolidBrush(RGB(171, 171, 171));
RegisterClassW(&wc);
CreateWindowW(wc.lpszClassName, L"window",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
100, 100, 330, 270, NULL, 0, hInstance, 0);
while (GetMessage(&msg, NULL, 0, 0))
TranslateMessage(&msg);
DispatchMessage(&msg);
DeleteObject(hBrush1);
DeleteObject(hBrush2);
return (int) msg.wParam;
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam)
switch(msg)
case WM_CREATE:
createWindow1(hwnd);
createWindow2(hwnd);
break;
case WM_KEYUP:
case WM_KEYDOWN:
POINT p;
if(GetCursorPos(&p))
HWND control = WindowFromPoint(p);
if(control)
int len = GetWindowTextLength(control);
wchar_t buffer[len + 1];
GetWindowText(control, buffer, sizeof(buffer));
MessageBox(NULL, buffer, L"", MB_OK);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
return DefWindowProcW(hwnd, msg, wParam, lParam);
void createWindow1(HWND hOwner)
WNDCLASSW wc = 0;
wc.lpszClassName = L"window2";
wc.hInstance = NULL;
wc.hbrBackground = hBrush1;
wc.lpfnWndProc = WndProc1;
wc.hCursor = LoadCursor(0, IDC_ARROW);
RegisterClassW(&wc);
CreateWindowW(wc.lpszClassName, L"window2",
WS_VISIBLE | WS_TABSTOP | WS_CHILD | WS_EX_CONTROLPARENT,
5, 5, 200, 100,
hOwner, 0, NULL, 0);
void createWindow2(HWND hOwner)
WNDCLASSW wc = 0;
wc.lpszClassName = L"window3";
wc.hInstance = NULL;
wc.hbrBackground = hBrush2;
wc.lpfnWndProc = WndProc2;
wc.hCursor = LoadCursor(0, IDC_ARROW);
RegisterClassW(&wc);
CreateWindowW(wc.lpszClassName, L"window3",
WS_VISIBLE | WS_TABSTOP | WS_CHILD,
5, 120, 200, 100,
hOwner, 0, NULL, 0);
LRESULT CALLBACK WndProc1(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
switch(msg)
case WM_CREATE:
CreateWindow(L"Button", L"Button A",
WS_VISIBLE | WS_TABSTOP | WS_CHILD,
5, 5, 80, 25,
hwnd, 0, NULL, 0);
break;
case WM_KEYUP:
case WM_KEYDOWN:
MessageBox(NULL, L"hello from proc1", L"", MB_OK);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
return DefWindowProcW(hwnd, msg, wParam, lParam);
LRESULT CALLBACK WndProc2(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
switch(msg)
case WM_CREATE:
CreateWindow(L"Button", L"Button ",
WS_VISIBLE | WS_TABSTOP | WS_CHILD,
5, 5, 80, 25,
hwnd, 0, NULL, 0);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
return DefWindowProcW(hwnd, msg, wParam, lParam);
【问题讨论】:
Those who do not understand the dialog manager are doomed to reimplement it, badly. 【参考方案1】:您可以在子窗口中使用SetFocus
。
一些代码:
case WM_KEYDOWN:
POINT p;
HWND control = GetFocus();
if (control)
int len = GetWindowTextLength(control);
wchar_t* buffer = new wchar_t[len + 1];
GetWindowText(control, buffer, len + 1);
MessageBox(NULL, buffer, L"", MB_OK);
break;
LRESULT CALLBACK WndProc1(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
switch (msg)
case WM_CREATE:
CreateWindow(L"Button", L"Button A",
WS_VISIBLE | WS_TABSTOP | WS_CHILD,
5, 5, 80, 25,
hwnd, 0, NULL, 0);
break;
case WM_LBUTTONDOWN:
SetFocus(hwnd);
break;
case WM_KEYUP:
case WM_KEYDOWN:
MessageBox(NULL, L"hello from proc1", L"", MB_OK);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
return DefWindowProcW(hwnd, msg, wParam, lParam);
也可以使用current方法,然后使用PostMessage/SendMessage将WM_KEYDOWN
消息转发到子窗口:
if (control)
int len = GetWindowTextLength(control);
wchar_t* buffer = new wchar_t[len + 1];
GetWindowText(control, buffer, len + 1);
MessageBox(NULL, buffer, L"", MB_OK);
PostMessage(hwnd_child, msg, wParam, lParam);
【讨论】:
Replaying input is not the same as reprocessing it.以上是关于如何检测子窗口上的 WM_KEYDOWN?的主要内容,如果未能解决你的问题,请参考以下文章