3

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

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

效果是,如果我有几个 MDI 子窗口相互部分重叠,我就不能像往常一样把一个窗口放在前面。这似乎与焦点无关 - MDI 子窗口可以具有焦点,但仍位于另一个 MDI 子窗口之后。

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

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

我怀疑窗口消息 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.

解决方案

MdiParent在显示窗口之前未设置其中一种形式的设置。

4

2 回答 2

3

以下是一些尝试的建议:

  • 向子表单添加点击事件,并在点击调用Show()时将表单置于最前面
  • 确保MdiParent在所有子窗体上设置属性
  • 确保IsMdiContainer在父表单上设置属性
  • WindowState子窗体集Normal
  • 用于Activate()激活表单并赋予其焦点

您还可以尝试利用父级的 z 顺序将焦点放在子级上:

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

希望这些建议中的一项或多项能够解决您的问题。

于 2011-10-19T20:15:28.463 回答
1

这个问题的答案可能比你所分享的更深于你的应用程序的实现。

您是否在使用任何 3rd 方 UI 库?(例如:DevExpress 或 Telerik 或 ...)这些库通常使用 pinvoke win32 api 来实现一些外观精美的窗口和/或简洁的功能。如果您使用的是普通的旧 Winform,那很高兴知道。

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

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

与此线程上当前的其他建议相反,您不应将 ShowDialog() 用于 Mdi Children。(如果您尝试使用分配给表单的 MdiParent 来显示对话框,我什至希望 Microsoft 抛出异常)。MdiChildren 不允许成为模态窗口,这很可能会导致您看到的那种不良行为。例如:一种形式想要成为模态/顶部,而普通的子形式试图获得焦点。

于 2011-10-20T03:30:34.883 回答