Как я могу контролировать, какое окно в настоящее время имеет фокус клавиатуры
Есть ли способ отслеживать, какое окно в настоящее время имеет фокус клавиатуры. Я мог обрабатывать WM_SETFOCUS для каждого окна, но мне интересно, есть ли альтернативный, более простой метод (т.е. Один обработчик сообщения где-нибудь).
Я мог бы использовать OnIdle() в MFC и вызывать GetFocus(), но это кажется немного взломанным.
Ответы
Ответ 1
Итак, из того, как вы сформулировали вопрос, я предполагаю, что вы хотите иметь обработчик событий, который вызывается всякий раз, когда фокус переключается между окнами. Вы хотите получить уведомление, а не опросить.
На самом деле я не думаю, что вызов GetFocus из OnIdle - это большая часть взлома - уверена, что это опрос, но это низкий опрос без побочных эффектов - но если вы действительно хотите отслеживать это, Windows Hooks, вероятно, ваш лучший выбор. В частности, вы можете установить крючок CBT (WH_CBT) и прослушать уведомление HCBT_SETFOCUS.
Windows вызывает крюк WH_CBT с помощью этого кода hook, когда Windows собирается установить фокус в любое окно. В случае крючков, специфичных для потока, окно должно принадлежать потоку. Если функция фильтра возвращает TRUE, фокус не изменяется.
Вы также можете использовать крючок WH_CALLWNDPROC и прослушивать сообщение WM_SETFOCUS.
В зависимости от того, сделаете ли вы глобальный крючок или приложение-локальным, вы можете отслеживать фокус во всех окнах в системе или только в окнах, принадлежащих вашему процессу.
Ответ 2
Как насчет Win32 GetForegroundWindow?
Ответ 3
Существует простой способ использования .Net Framework 3.5: библиотека Автоматизация пользовательского интерфейса позволяет изменить фокус событий, который срабатывает при каждом изменении фокуса на новый элемент управления.
Страница в MSDN
Пример:
public void SubscribeToFocusChange()
{
AutomationFocusChangedEventHandler focusHandler
= new AutomationFocusChangedEventHandler(OnFocusChanged);
Automation.AddAutomationFocusChangedEventHandler(focusHandler);
}
private void OnFocusChanged(object src, AutomationFocusChangedEventArgs e)
{
AutomationElement focusedElement = sender as AutomationElement;
//...
}
Этот api на самом деле использует крючки для Windows за кулисами, чтобы сделать это. Однако вы должны использовать .Net Framework...
Ответ 4
Если вы программируете в .net 3.5, пакет автоматизации olorin является самым легким, но остерегайтесь использовать его в программе, которая сама имеет пользовательский интерфейс, по крайней мере, если пользовательский интерфейс выполняется в WPF - фокусы отслеживания фокуса путаются событиями в собственном приложении и быстро блокируют пользовательский интерфейс. Я отправил MS на отчет об ошибке. Я не заметил ту же проблему, используя традиционный пользовательский интерфейс Windows Forms. Конечно, вы могли бы поместить код отслеживания в отдельное консольное приложение и использовать какой-то ipc для передачи необходимой вам информации.
Заманчивая альтернатива использования Interop для доступа к WH_CBT Windows Hook с С# не будет работать - единственные глобальные перехватчики, с которыми вы можете перейти с С#, - это мыши и клавиатуры.
Ответ 5
Вы можете отслеживать сообщения для события WM_ACTIVATE.
ref
Ответ 6
Ну, это может быть не очень изящно... но вы можете легко получить текущий сфокусированный элемент управления. Таким образом, вы можете подумать о настройке таймера, который запрашивает каждые полсекунды или около того "Где находится текущий фокус?"... Затем вы можете наблюдать изменения. Пример кода Delphi приведен ниже; его довольно легко адаптировать, поскольку настоящая работа выполняется в вызовах Windows API.
<snip>
function TForm1.GetCurrentHandle: integer;
var
activeWinHandle: HWND;
focusedThreadID : DWORD;
begin
//return the Windows handle of the currently focused control
Result := 0;
activeWinHandle := GetForegroundWindow;
focusedThreadID := GetWindowThreadProcessID(activeWinHandle,nil);
if AttachThreadInput(GetCurrentThreadID,focusedThreadID,true) then begin
try
Result := GetFocus;
finally
AttachThreadInput(GetCurrentThreadID, focusedThreadID, false);
end;
end; //if attached
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
//give notification if the handle changed
//(this code gets fired by a timer)
CurrentHandle := GetCurrentHandle;
if CurrentHandle <> PreviousHandle then begin
Label1.Caption := 'Last focus change occurred @ ' + DateTimeToStr(Now);
end;
PreviousHandle := CurrentHandle;
end;
<snip>
Ответ 7
http://msdn.microsoft.com/en-us/library/ms771428.aspx
Имеет образец отслеживания фокуса окна.