有没有办法跟踪哪个窗口当前有键盘焦点。我可以为每个窗口处理WM_SETFOCUS,但我想知道是否有一个替代的,更简单的方法(即某处的单个消息处理程序)。
我可以在MFC中使用OnIdle()并调用GetFocus(),但这看起来有点hacky。
答案 0 :(得分:17)
因此,从你提出问题的方式来看,我推断你想要一个事件处理程序,只要焦点在windows之间切换就会调用它。您希望收到通知,而不是必须进行投票。
我实际上并不认为从OnIdle调用GetFocus是一个很大的问题 - 确定它是轮询,但它是低开销的轮询而没有副作用 - 但是如果你真的想跟踪它,Windows Hooks可能是你最好的选择。具体来说,您可以安装CBT挂钩(WH_CBT)并监听HCBT_SETFOCUS通知。
当Windows即将焦点设置为任何窗口时,Windows会使用此挂钩代码调用WH_CBT挂钩。在特定于线程的钩子的情况下,窗口必须属于线程。如果过滤器函数返回TRUE,则焦点不会改变。
您也可以使用WH_CALLWNDPROC挂钩并侦听WM_SETFOCUS消息。
根据您是将其设置为全局挂钩还是应用程序本地,您可以在系统上的所有窗口中跟踪焦点,或仅跟踪您的流程所拥有的窗口。
答案 1 :(得分:3)
Win32 GetForegroundWindow怎么样?
答案 2 :(得分:3)
使用.Net Framework 3.5有一种简单的方法:库 UI自动化提供了一个事件焦点更改,每次焦点更改为新控件时都会触发。
样品:
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 hook来做到这一点。但是,您必须使用.Net Framework ...
答案 3 :(得分:1)
如果您使用.net 3.5进行编程,那么自动化软件包olorin提到是最简单的,但要注意在自己有UI的程序中使用它,至少如果UI是在WPF中完成的 - 焦点跟踪钩子会被自己的应用程序中的事件搞糊涂,并快速锁定UI。我向MS发送了bug report。我没有使用传统的Windows窗体UI观察到同样的问题。当然,您可以将跟踪代码放在一个单独的控制台应用程序中,并使用某种ipc来传输您需要的信息。
使用Interop从C#访问WH_CBT Windows Hook的诱人选择将无效 - the only global hooks you can get at from C# are the mouse and keyboard。
答案 4 :(得分:0)
您可以监控WM_ACTIVATE事件的消息。
答案 5 :(得分:0)
嗯,这可能不是很优雅......但是你可以很容易地检索当前聚焦控件。所以你可以考虑设置一个每1/2秒左右询问一次的计时器“当前焦点在哪里?”......然后你可以观察到变化。示例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>
答案 6 :(得分:0)
http://msdn.microsoft.com/en-us/library/ms771428.aspx
有一个窗口焦点跟踪器样本。