Mfc 在资源对话框中定位与另一个控件重叠的动态创建的控件会导致异常行为

Mfc 在资源对话框中定位与另一个控件重叠的动态创建的控件会导致异常行为,mfc,Mfc,我试图在运行时在资源对话框上创建一个动态自定义控件,该控件与现有控件重叠。然而,当我这样做时,它会导致一个奇怪的工件 如果我将新控件放置在z顺序中的另一个控件之后,我的动态控件将绘制在资源控件的顶部,正如我所期望的那样。但是,如果单击两个控件之间共享的点,它将选择资源控件 如果我在z顺序中将新控件放在另一个控件之前,那么我的动态控件将被资源控件所覆盖,这也是预期的。但是,如果我再次单击它们之间共享的点,它将选择新控件 我所期望的是,z顺序顶部的控件会有指向它们的任何单击。实际结果与直觉相反。为什

我试图在运行时在资源对话框上创建一个动态自定义控件,该控件与现有控件重叠。然而,当我这样做时,它会导致一个奇怪的工件

如果我将新控件放置在z顺序中的另一个控件之后,我的动态控件将绘制在资源控件的顶部,正如我所期望的那样。但是,如果单击两个控件之间共享的点,它将选择资源控件

如果我在z顺序中将新控件放在另一个控件之前,那么我的动态控件将被资源控件所覆盖,这也是预期的。但是,如果我再次单击它们之间共享的点,它将选择新控件

我所期望的是,z顺序顶部的控件会有指向它们的任何单击。实际结果与直觉相反。为什么会这样

作为一个代码示例,我创建了一个MFC对话框应用程序,其中对话框使用两个列表框来删除任何自定义控件错误的问题。将一个列表框添加到id为IDC\U LIST1且成员变量名为m\U dlgResCtrl的资源中。第二个具有成员变量名
mdlgaddedctrl
。将以下代码添加到
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
的z顺序号为2。
Cancel
显示在
OK
的上方,但是当在重叠区域中单击时,
OK
是被单击的。

您混淆了z顺序和绘图顺序。它们不一定有联系

在z顺序中位于另一个窗口之前的窗口位于另一个窗口之上

因此,该代码实际上将
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);
// 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);
此代码实际上将
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);
// 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
消息到达的任何顺序自由绘制彼此

要解决此问题,只需将样式添加到每个可能与其他子窗口重叠的子窗口:

从MSDN:

当某个特定的 子窗口接收WM_PAINT消息,即WS_CLIPSIBLINGS样式 将所有其他重叠的子窗口从窗口区域中剪切出去 要更新的子窗口。如果未指定WS_CLIPSIBLINGS,并且 在客户端内绘制时,子窗口可能重叠 子窗口的区域,在 相邻的子窗口


啊,我明白了。我本以为他们会像我想象的那样更加直观。为什么微软会走非直觉路线?他们的疯狂有什么办法吗?或者只是一个随机的/糟糕的设计选择?我也没有意识到在z顺序中1比2高。很高兴知道。我本以为paint消息会从1开始迭代。。N、 导致更高的数字在z顺序中更高。(我使用1只是因为对话框编辑器使用它)“为什么MS会走非直观的路线?”——可能是因为效率。回到Windows GUI发明的年代,人们必须在任何地方节省计算资源。重叠的子窗口不是很常见,所以总是考虑绘图的Z顺序会是一种浪费资源。但是必须发送消息的顺序。以伪随机顺序发送消息如何更有效?它不是随机顺序。当所有子窗口都无效时,系统可能会按z顺序重新绘制它们。这可以从您的屏幕截图中看到。首先重绘最上面的窗口,然后重绘下一个较低的窗口,并在最上面的窗口上绘制。但是,如果只有一些窗口无效,则只有这些窗口将被重新绘制,结果可能会出现错误。