激活时MDI窗口的z顺序异常

本文关键字:顺序 异常 窗口 MDI 激活 | 更新日期: 2023-09-27 18:11:19

我有一个有许多MDI子窗口的应用程序。通常,用户可以通过单击窗口的客户端和非客户端区域将一个MDI子窗口带到前面。这似乎通常发生在鼠标按钮一按下。

现在,有时发生的情况是,当用户单击其客户机区域上的MDI子窗体时,窗口没有而不是按预期出现在前面。但是,单击表单的标题栏确实会将窗口带到前面,但只有在释放鼠标按钮时才会这样。这样做的效果是,用户可以将一个MDI子窗口拖到另一个的后面,当释放鼠标按钮时,被拖拽的窗口会移到前面。

效果是,如果我有几个MDI子窗口部分地相互重叠,我就不能像通常那样把一个窗口放在前面。这似乎与焦点无关——一个MDI子窗口可以有焦点,但仍然在后面的另一个MDI子窗口。

此外,这似乎是随机发生的,在使用应用程序一段时间后。我可以使用从用户发送的序列化程序状态("保存"文件)来重现错误。

我的问题有2部分:任何想法为什么会发生这种情况,以及我如何调试我的程序,以找出为什么会发生这种情况?

我怀疑窗口消息WM_ACTIVATE(或类似的东西)没有被正确处理,但这是一个c#应用程序,我没有对消息队列做任何不寻常的事情。

编辑:这里有一些来自spy++的额外信息。

下面是spy++在一切正常运行时的输出:

<00013> 00D209AA S WM_PARENTNOTIFY fwEvent:WM_LBUTTONDOWN xPos:146 yPos:147
<00014> 00D209AA R WM_PARENTNOTIFY
<00015> 00D209AA S WM_WINDOWPOSCHANGING lpwp:0012EE90
<00016> 00D209AA R WM_WINDOWPOSCHANGING
<00017> 00D209AA S WM_CHILDACTIVATE
<00018> 00D209AA S WM_NCPAINT hrgn:D3043A75
<00019> 00D209AA R WM_NCPAINT
<00020> 00D209AA S WM_ERASEBKGND hdc:C20124F7
<00021> 00D209AA S WM_GETTEXTLENGTH
<00022> 00D209AA R WM_GETTEXTLENGTH cch:1
<00023> 00D209AA S WM_GETTEXT cchTextMax:4 lpszText:0012DC48
<00024> 00D209AA R WM_GETTEXT cchCopied:1 lpszText:0012DC48 (" ")
<00025> 00D209AA R WM_ERASEBKGND fErased:True
<00026> 00D209AA S WM_WINDOWPOSCHANGING lpwp:0012EB80
<00027> 00D209AA R WM_WINDOWPOSCHANGING
<00028> 00D209AA S WM_MDIACTIVATE hwndDeactivate:014809AE hwndActivate:00D209AA (activating)
<00029> 00D209AA S WM_NCACTIVATE fActive:True
<00030> 00D209AA R WM_NCACTIVATE
<00031> 00D209AA S WM_IME_SETCONTEXT fSet:1 iShow:C000000F
<00032> 00D209AA R WM_IME_SETCONTEXT
<00033> 00D209AA S WM_SETFOCUS hwndLoseFocus:00B20A2A
<00034> 00D209AA R WM_SETFOCUS
<00035> 00D209AA R WM_MDIACTIVATE
<00036> 00D209AA R WM_CHILDACTIVATE
<00037> 00D209AA S WM_WINDOWPOSCHANGED lpwp:0012EE90
<00038> 00D209AA R WM_WINDOWPOSCHANGED
<00039> 00D209AA S WM_MOUSEACTIVATE hwndTopLevel:012C093A nHittest:HTCLIENT uMsg:WM_LBUTTONDOWN
<00040> 00D209AA R WM_MOUSEACTIVATE fuActivate:MA_ACTIVATE

当我运行带有重现错误的应用程序获得输出时,单击客户端区域会产生以下内容:

<01315> 023E0AA0 S WM_PARENTNOTIFY fwEvent:WM_LBUTTONDOWN xPos:139 yPos:142
<01316> 023E0AA0 R WM_PARENTNOTIFY
<01317> 023E0AA0 S WM_MOUSEACTIVATE hwndTopLevel:012C093A nHittest:HTCLIENT uMsg:WM_LBUTTONDOWN
<01318> 023E0AA0 R WM_MOUSEACTIVATE fuActivate:MA_ACTIVATE

查看消息编号,我可以立即看到有一堆消息没有发生,特别是WM_CHILDACTIVATE

<<p> 解决方案/strong> 在窗口显示之前,其中一个窗体的

MdiParent没有被设置

激活时MDI窗口的z顺序异常

这里有一些建议可以尝试:

  • 为子窗体添加一个点击事件,点击时调用Show()将窗体带到前面
  • 确保在所有子窗体上设置MdiParent属性
  • 确保在父窗体上设置了IsMdiContainer属性
  • 将子窗体的WindowState设置为Normal
  • 使用Activate()激活表单并给它焦点

你也可以尝试利用父元素的z轴顺序将焦点放在子元素上:

this.ActiveMdiChild.SendToBack();
Control.ControlCollection ct = ((MdiClient)this.ActiveMdiChild.Parent).Controls;
((Form)ct[0]).Activate();

希望这些建议中的一个或多个能解决你的问题。

这个问题的答案可能隐藏在您的应用程序的实现中,而不是您所分享的。

您是否使用任何第三方UI库?(例如:DevExpress或Telerik或…)这些库通常使用pinvoke win32 api来实现一些漂亮的蒙皮窗口和/或简洁的功能。如果您使用的是普通的旧winforms,那么最好了解一下。

您可以在保存的应用程序状态中重现问题,这表明在创建子窗口的方式中存在错误。我将逐步加载这个已保存的状态文件,以查看是否所有子窗口都以相同的方式加载。这是一种可以归结为某一行代码的问题。

此外,如果您可以收集重现问题的多个保存状态文件,您可能能够确定趋势。也许是应用程序中的某个特定窗口始终显示上述行为?

与当前在此线程上的其他建议相反,您不应该为Mdi Children使用ShowDialog()。(我甚至希望微软抛出一个异常,如果你试图ShowDialog与MdiParent分配给表单)。子窗口不允许是模态窗口,这很可能导致您看到的那种不良行为。示例:一个窗体想要成为modal/ontop,而另一个正常的子窗体想要获得焦点