原因:因为切换窗口再切换回来后,窗口会有一个WM_SETFOCUS消息,但是DUILIB未对这个消息进行处理,因此窗口下面的所有子控件也不能获取到这个WM_SETFOCUS事件,自然不会做出响应。
解决方法:在CPaintManagerUI::MessageHandler的消息处理函数添加一个分支,响应WM_SETFOCUS,这里不能直接调用m_pFocus->SetFocus(),因为这个SetFocus函数里会判断当前的焦点控件和设置进行的焦点控件是否相同,如果相同就不会处理,因为在切换窗口时,DUILIB里保存了焦点控件,因此需要使用下面的方法。
case WM_SETFOCUS:
{
TEventUI event = { 0 };
event.Type = UIEVENT_SETFOCUS;
event.pSender = m_pFocus;
event.dwTimestamp = ::GetTickCount();
m_pFocus->Event(event);
return true;
}
这里虽然解决了切换窗口保持焦点的问题,但是又产生了新的问题,就是在用TAB切换有些控件时,要两次才能切换成功,原因是因为调用SetFocus后,会调用::SetFocus(m_hWndPaint),原焦点控件又会得到一次WM_SETFOCUS,因此把函数void CPaintManagerUI::SetFocus(CControlUI* pControl, bool bFocusWnd)的::SetFocus(m_hWndPaint);放到切换焦点后再执行,如下:
if( pControl != NULL
&& pControl->GetManager() == this
&& pControl->IsVisible()
&& pControl->IsEnabled() )
{
DUITRACE("Set focus to new control");
m_pFocus = pControl;
TEventUI event = { 0 };
event.Type = UIEVENT_SETFOCUS;
event.pSender = pControl;
event.dwTimestamp = ::GetTickCount();
m_pFocus->Event(event);
SendNotify(m_pFocus, DUI_MSGTYPE_SETFOCUS);
::SetFocus(m_hWndPaint);
}
通过在WM_SETFOCUS响应里打印当前控件的名称,发现在切换窗口时,窗口的焦点会切换到下一个控件上,切换窗口回自然会错,通过搜索SetNextTabControl查看哪些地方调用了这个函数,发现在CPaintManagerUI::MessageHandler的WM_PAINT里最可疑,在绘制窗体时,
if( m_bFocusNeeded) {
SetNextTabControl();
}
在将窗体最小化时再恢复会发送UIEVENT_WINDOWSIZE,这个里面UIEdit会调用SetFocusNeeded,使m_bFocusNeeded为TRUE,然后重绘的时候,就会SetNextTabControl切换到下一个焦点,导致文本框失败去焦点。初始分析这个代码可能是设置默认焦点的,因此将代码移动到下面
if( m_bFirstLayout ) {
m_bFirstLayout = false;
if (m_bFocusNeeded) {
SetNextTabControl();
}
SendNotify(m_pRoot, DUI_MSGTYPE_WINDOWINIT, 0, 0, false);
if( m_bLayered && m_bLayeredChanged ) {
Invalidate();
SetPainting(false);
return true;
}
}