我已经使用 C 和 Windows API 创建了一个 Windows GUI 程序,并且我希望该程序利用键盘加速器。我已经设置了一些可以正常工作的加速器,但是当焦点转到我的程序主窗口的子窗口时,例如列表视图控件或状态栏控件,键盘加速器似乎正在转换为 WM_COMMAND 消息对于子窗口而不是主窗口。因此,当焦点位于子控件上时,我在主窗口的 WndProc 中对相应 WM_COMMAND 消息的处理将被忽略。
我应该如何解决这个问题?
我已经使用 C 和 Windows API 创建了一个 Windows GUI 程序,并且我希望该程序利用键盘加速器。我已经设置了一些可以正常工作的加速器,但是当焦点转到我的程序主窗口的子窗口时,例如列表视图控件或状态栏控件,键盘加速器似乎正在转换为 WM_COMMAND 消息对于子窗口而不是主窗口。因此,当焦点位于子控件上时,我在主窗口的 WndProc 中对相应 WM_COMMAND 消息的处理将被忽略。
我应该如何解决这个问题?
我找到了答案。主窗口的子窗口必须是子类的,这样键盘加速器产生的 WM_COMMAND 消息才能被截获并传递给父窗口。
这涉及将控件的窗口过程更改为不同的。备用过程通过将它们发送到父窗口来处理应该被截获的消息。指向原始窗口过程的指针也必须存储在某处,以便控件可以正常工作。
可以使用带有 GWLP_WNDPROC 的SetWindowLongPtr更改窗口过程。
下面是一个简单的示例,说明如何通过在控件的用户数据值 (GWLP_USERDATA) 中存储指向原始窗口过程的指针来执行此操作:
更改窗口过程并将原始过程存储在 GWLP_USERDATA 中的代码:
SetWindowLongPtr( hWnd, GWLP_USERDATA, ( LONG_PTR )SetWindowLongPtr( hWnd, GWLP_WNDPROC, ( LONG_PTR )WndProc ) );
截取窗口过程:
static LRESULT CALLBACK WndProc( const HWND hWnd, const UINT message, const WPARAM wParam, const LPARAM lParam )
{
switch( message )
{
case WM_COMMAND:
SendMessage( GetParent( hWnd ), message, wParam, lParam );
return 0;
default:
//Assume that GWLP_USERDATA has been set to the original window procedure.
return CallWindowProc( ( WNDPROC )GetWindowLongPtr( hWnd, GWLP_USERDATA ), hWnd, message, wParam, lParam );
}
}
另一种方法是避免对子窗口使用 TranslateAccelerator,示例代码:
if (mainWidget() && msg.hwnd == mainWidget()->hwnd()) {
if (TranslateAccelerator(msg.hwnd, hMainAccelTable, &msg)) {
continue;
}
}
TranslateMessage(&msg);
DispatchMessage(&msg);
如果消息不是主小部件的,我们不使用主小部件的加速器表为它翻译加速器。