Ускорители клавиатуры Windows и дочерние окна
Я создал программу с графическим интерфейсом Windows, используя C и Windows API, и хочу, чтобы программа использовала ускорители клавиатуры. Я настроил некоторые ускорители, которые работают правильно, но когда фокус переходит к дочернему окну главного окна моей программы, например элементу управления списком или строке состояния, кажется, что клавиатурные ускорители переводятся в сообщения WM_COMMAND для дочерних окон, а не главного окна. Из-за этого моя обработка соответствующих сообщений WM_COMMAND в WndProc главного окна игнорируется, когда фокус находится на дочернем элементе управления.
Как мне решить эту проблему?
2 ответа
Я нашел ответ. Дочерние окна главного окна должны быть разделены на подклассы, чтобы сообщения WM_COMMAND, генерируемые ускорителями клавиатуры, могли быть перехвачены и переданы в родительское окно.
Это включает в себя изменение оконной процедуры элемента управления на другую. Альтернативная процедура обрабатывает сообщения, которые должны быть перехвачены, отправляя их в родительское окно. Указатель на исходную оконную процедуру также должен храниться где-то, чтобы элемент управления мог функционировать правильно.
Оконную процедуру можно изменить с помощью SetWindowLongPtr с GWLP_WNDPROC.
Вот простой пример того, как сделать это путем сохранения указателя на исходную оконную процедуру в значении пользовательских данных элемента управления (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);
если сообщение не относится к mainWidget, мы не переводим ускоритель для него, используя таблицу ускорителей основного виджета.