【LibUIDK界面库系列文章】使用双窗口制作阴影边框时的激活问题

作者:刘树伟

在使用LibUIDK界面库为客户制作一个类似桌面版微信界面的时候,采用了双窗口来制作边框阴影。在关闭了登录对话框后,弹出的主界面未被激活到z-order的最前端。下面的场景及解决方案。

当使用双窗口来制作阴影窗口时,可以先创建阴影窗口,再由阴影窗口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);
不行。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值