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

时间:2017-12-02 17:19:46

标签: mfc

我正在尝试在运行时在资源对话​​框上创建动态自定义控件,该控件与现有控件重叠。但是,当我这样做时,它会导致一个奇怪的神器。

如果我将新控件放在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控制之后放置:

When placed after res control

点击共享空间后:

After clicking on shared space

在res控制之前放置:

When placed before res control

点击共享空间后:

After clicking on shared space

注意:此行为不仅限于动态控件。只需移动OK按钮控件以重叠Cancel按钮控件即可显示同样的问题。 OK的z序号为1,Cancel为2. Cancel显示在OK的顶部,但在重叠区域中点击{ {1}}是被点击的。

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,则   子窗口重叠,有可能,在客户端内绘制时   子窗口的区域,在客户区域内绘制   邻近的儿童窗口。