當使用雙窗口來製作陰影窗口時,可以先創建陰影窗口,再由陰影窗口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);
不行。