当使用双窗口来制作阴影窗口时,可以先创建阴影窗口,再由阴影窗口DoModal出工作窗口。常见的例子是:先创建一个阴影窗口,然后阴影窗口DoModal出登录对话框。当点击登录按钮登录成功后,登录对话框返回,接着弹出主界面。为了让阴影窗口和工作窗口同步移动、隐藏、显示、TopMost等,封装成了下面的接口:
// 当工作窗口位置改变后,让阴影窗口的位置同步改变
// 本函数在工作窗口的WM_WINDOWPOSCHANGED消息中调用。
// lprcMargin: 工作窗口相对于阴影窗口的边距
// lpsizeRoundRect: 如果工作窗口有圆角,设置圆角大小
int LibUIDK::SyncHostWindow(CUIWnd *pWorkWnd, UINT uWorkWndFlags, LPCRECT lprcMargin, LPSIZE lpsizeRoundRect)
{
if (pWorkWnd == NULL || lprcMargin == NULL)
{
ASSERT(FALSE);
return -1;
}
CWnd *pParent = pWorkWnd->GetParent();
if (pParent == NULL)
{
return 1;
}
// 阴影窗口同步隐藏显示
if (IsIncludeFlag(uWorkWndFlags, SWP_HIDEWINDOW))
{
pParent->ShowWindow(SW_HIDE);
}
else if (IsIncludeFlag(uWorkWndFlags, SWP_SHOWWINDOW))
{
pParent->ShowWindow(SW_SHOW);
}
// 虽然通过把工作窗口当成阴影窗口的模式对话框,可以解决第三方窗口嵌入问题。
// 但当工作窗口为topmost时,仍然会导致第三方窗口嵌入,解决方案就是让阴影窗口也变成topmost。
// 如果为工作窗口设置了Topmost,那么同时也为阴影窗口设置Topmost。
// 当阴影窗口为Topmost时,即使没有为工作窗口设置Topmost,系统也会自动为工作窗口加上的。
// 所以在为阴影窗口设置Topmost时,没必要清除工作窗口的WS_EX_TOPMOST属性。
LONG lExStyle = GetWindowLong(pWorkWnd->GetSafeHwnd(), GWL_EXSTYLE);
if ((lExStyle & WS_EX_TOPMOST) == WS_EX_TOPMOST)
{
LONG lParentExStyle = GetWindowLong(pWorkWnd->GetParent()->GetSafeHwnd(), GWL_EXSTYLE);
if ((lParentExStyle & WS_EX_TOPMOST) == 0)
{
pWorkWnd->GetParent()->SetWindowPos(&CWnd::wndTopMost,
0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
}
}
// 阴影窗口同步跟随代码
CRect rcWnd;
pWorkWnd->GetWindowRect(rcWnd);
rcWnd.InflateRect(lprcMargin);
pParent->MoveWindow(rcWnd);
// 如果有圆角,设置rgn
if (lpsizeRoundRect != NULL && lpsizeRoundRect->cx != 0 && lpsizeRoundRect->cy != 0)
{
CRect rcClient;
pWorkWnd->GetClientRect(rcClient);
CRgn rgn;
rgn.CreateRoundRectRgn(0, 0, rcClient.right + 1, rcClient.bottom + 1, 4, 4);
pWorkWnd->SetWindowRgn((HRGN)rgn.GetSafeHandle(), TRUE);
rgn.DeleteObject();
}
return 0;
}
但是,在工作窗口的WM_WINDOWPOSCHANGED消息中调用。在工作窗口的DoModal内部,在RunModalLoop后,会调用如下代码让工作窗口先隐藏掉:
SetWindowPos(NULL, 0, 0, 0, 0, SWP_HIDEWINDOW |
SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
如果这时,隐藏阴影窗口。会导致接下来弹出的窗口,未激活到最前端。 针对上面的例子,主界面在显示时,可能被另一个窗口挡住。甚至调用BringWindowToTop、SetActiveWindow 都无法激活。
解决方案是:
在pParent->ShowWindow(SW_HIDE);隐藏阴影窗口之前,先调用pParent->SetActiveWindow();或pParent->SetForegroundWindow()把阴影窗口激活(推荐使用SetForegroundWindow,因为托盘图标右键菜单也是类似问题,微软官方使用SetForegroundWindow)。
经测试:pParent->SetActiveWindow()与pParent->ShowWindow(SW_HIDE)或者pParent->SetActiveWindow()与pParent->SetWindowPos(NULL, 0, 0, 0, 0, SWP_HIDEWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER)都是可以的。
但pParent->SetActiveWindow();与
LONG_PTR lStyle = IUIGetWindowLong(pParent->GetSafeHwnd(), GWL_STYLE);
lStyle &= ~WS_VISIBLE;
IUISetWindowLong(pParent->GetSafeHwnd(), GWL_STYLE, lStyle);
不行。