我正在尝试在运行时在资源对话框上创建动态自定义控件,该控件与现有控件重叠。但是,当我这样做时,它会导致一个奇怪的神器。
如果我将新控件放在z顺序中的另一个控件之后,我的动态控件将被绘制在资源控件的顶部,正如我预期的那样。但是,如果我单击两个控件之间共享的位置,它将选择资源控件。
如果我将新控件放在z顺序中的另一个控件之前,我的动态控件将再次按预期由资源控件绘制。但是,如果我再次单击它们之间共享的位置,它将选择新控件。
我所期望的是,z-order顶部的控件会有任何针对它们的点击。实际结果是反直觉的。为什么会这样?
作为代码示例,我创建了一个MFC对话框应用程序,其中对话框使用两个列表框来删除任何自定义控件错误的任何问题。将一个列表框添加到标识为IDC_LIST1
且成员变量名为m_dlgResCtrl
的资源中。第二个具有成员变量名m_dlgAddedCtrl
。以下代码添加到OnInitDialog()
成员函数:
CRect rect;
m_dlgResCtrl.GetWindowRect(rect);
ScreenToClient(rect);
rect += CPoint(20, 20);
m_dlgAddedCtrl.Create(LBS_SORT | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP
, rect, this, IDC_LIST1 + 1);
m_dlgAddedCtrl.SetFont(GetFont());
// place before resource control in z-order
//m_dlgAddedCtrl.SetWindowPos(m_dlgResCtrl.GetWindow(GW_HWNDPREV), 0, 0, 0, 0
, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
// place after resource control in z-order
m_dlgAddedCtrl.SetWindowPos(&m_dlgResCtrl, 0, 0, 0, 0
, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
// added some text to show overlap
m_dlgResCtrl.AddString(L"Res ctrl");
m_dlgAddedCtrl.AddString(L"Added ctrl");
在res控制之后放置:
点击共享空间后:
在res控制之前放置:
点击共享空间后:
注意:此行为不仅限于动态控件。只需移动OK
按钮控件以重叠Cancel
按钮控件即可显示同样的问题。 OK
的z序号为1,Cancel
为2. Cancel
显示在OK
的顶部,但在重叠区域中点击{ {1}}是被点击的。
答案 0 :(得分:1)
您正在混淆z顺序和绘图顺序。它们不一定相关。
之前在z顺序中的另一个窗口之前的窗口是上面另一个窗口。 Reference
因此,此代码实际上将m_dlgAddedCtrl
定位在 m_dlgResCtrl
下方:
// place after resource control in z-order
m_dlgAddedCtrl.SetWindowPos(&m_dlgResCtrl, 0, 0, 0, 0
, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
此代码实际上将m_dlgAddedCtrl
置于 m_dlgResCtrl
之上:
// place before resource control in z-order
//m_dlgAddedCtrl.SetWindowPos(m_dlgResCtrl.GetWindow(GW_HWNDPREV), 0, 0, 0, 0
, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
考虑到这一点,两种情况下的点击行为都是正确的。在重叠区域中,最顶层的子窗口接收鼠标单击并获得焦点。
只有绘图顺序显示不正确。在绘制子窗口时,Windows不会自动尊重z顺序,这可能会让人感到惊讶!它只是向所有具有非空更新区域的子窗口发送WM_PAINT
消息,然后可以以WM_PAINT
消息到达的任何顺序自由地相互绘制。
要解决此问题,只需将WS_CLIPSIBLINGS
样式添加到可能与其他子窗口重叠的每个子窗口中:
来自MSDN:
某个特定的时候 子窗口接收WM_PAINT消息,即WS_CLIPSIBLINGS样式 剪辑所有其他重叠的子窗口 子窗口要更新。如果未指定WS_CLIPSIBLINGS,则 子窗口重叠,有可能,在客户端内绘制时 子窗口的区域,在客户区域内绘制 邻近的儿童窗口。