奇怪的错误在.NET 4.0 / 4.5的WinForms的MenuStrip偷焦点奇怪、错误、焦点、MenuStrip

2023-09-04 07:19:06 作者:空有执念了残生

我们最近升级到VS 2012是伴随着升级到.NET框架4.5。这里介绍一个陌生而讨厌的臭虫与WinForms的MenuStrip控件连接。同样的错误也发生在应用目标定位.NET Framework 4.0中4.5的安装程序明显更新的4.0份为好。因此,一个完美的工作code模式被打破现在的框架升级使然。

We recently upgraded to VS 2012 which is accompanied by an upgrade to .NET Framework 4.5. This introduces a strange and pesky bug in connection with WinForms MenuStrip control. The same bug also occurs in applications targetting .NET Framework 4.0 as 4.5's installer obviously updates parts of 4.0 as well. Therefore, a perfectly working code pattern is now broken just because of the framework upgrade.

问题描述: 我有一个Form1中带的MenuStrip。对于下拉项目之一,该事件处理程序打开另一个窗体2(不neccessarily一个孩子,只是另一种形式)。如果用户右击插入新的窗体2或它的一个子控件,这触发的ContextMenuStrip的展(),那么原来的Form1中持久性有机污染物进入前景再次。 出现这种情况的独立于窗体2所有previous其他UI操作。我们可以调整大小,移动,最小化,最大化窗体2,控制之间进行切换,输入文字等不知何故Form1的MenuStrip中似乎要记住,这导致开盘窗体2和争夺的重点首先右键单击。

Problem description: I have a Form1 with a MenuStrip. For one of the drop down items, the event handler opens another Form2 (not neccessarily a child, just another Form). If the user right-clicks into the new Form2 or one of its child controls and this triggers a Show() of a ContextMenuStrip, then the original Form1 pops into foreground again. This happens independent of all previous other UI actions in Form2. One can resize, move, minimize, maximize Form2, switch between controls, enter text etc. Somehow MenuStrip of Form1 seems to remember that it caused opening of Form2 and grabs focus on first right-click.

我是用不同的方法进行试验,但未能找到一个解决办法为止。星座的情况并不少见,可能会影响周围很多的WinForms应用程序。因此,我决定将它张贴在这里,因为一个可行的解决方案可能是普遍关心的。我会很高兴,如果有人知道一种解决办法,或至少有一些线索给我。

I was experimenting with different approaches but was unable to find a workaround so far. The constellation is not uncommon and might affect lots of WinForms applications around. Therefore I decided to post it here since a viable solution might be of general interest. I would be very glad if someone knows a workaround or has at least some clues for me.

我能够提炼出它的精华在以下code。它应该是可重复的任何机器与.NET 4.5安装,它打靶4.0和4.5各时出现 - 但不是在3.5或更低。

I was able to distill it's essence in the following code. It should be reproducible on any machine with .NET 4.5 installed and it occurs when targetting 4.0 and 4.5 each - but not on 3.5 or lower.

using System;
using System.Windows.Forms;

namespace RightClickProblem {
    static class Program {
        [STAThread]
        static void Main() {
            // Construct a MenuStrip with one item "Menu" with one dropdown-item "Popup"
            var mainMenu = new MenuStrip();
            var mainItem = new ToolStripMenuItem("Menu");
            mainItem.DropDownItems.Add(new ToolStripMenuItem("Popup", null, Popup));
            mainMenu.Items.Add(mainItem);

            // Create form with MenuStrip and Show
            var form1 = new Form();
            form1.Controls.Add(mainMenu);
            Application.Run(form1);
        }

        private static void Popup(object sender, EventArgs e) {
            // Create a form with a right click handler and show
            var form2 = new Form();
            var contextMenu = new ContextMenuStrip();
            contextMenu.Items.Add("Just an item...");
            form2.ContextMenuStrip = contextMenu;
            form2.Show();
            // Problem: contextMenu.Show() will give focus to form1
        }
    }
}

编辑:我花了一些时间,通过.NET Framework的源$ C ​​$ C步进,发现根本原因很可能在System.Windows.Forms.ToolStripManager。目前,微软正在使用一个消息过滤器来跟踪窗口激活它以某种方式不正确实施了的​​MenuStrip。 在此期间我还发现,微软已经在修补程序(请参见 http://support.microsoft不客气了这个问题。 COM / KB / 2769674 ),并希望这将找到自己的方式进入的.NET Framework 4.5的更新版本。

I spent some time stepping through .NET Framework source code and found the root cause to be very likely in System.Windows.Forms.ToolStripManager. There, Microsoft is using a message filter to track window activation which is somehow incorrectly implemented for MenuStrip. In the meantime I also found that Microsoft already adressed this issue in a hotfix (see http://support.microsoft.com/kb/2769674) and hopefully this will find its way into a future update of .NET Framework 4.5.

不幸的是难以执行此修复程序上的所有客户机上得到应用。因此,一个可行的解决办法仍然是很大的AP preciated。我自己已经无法迄今为止找到一个切实可行的解决方案...

Unfortunately it is difficult to enforce this hotfix to be applied on all client machines. Therefore a viable workaround would still be greatly appreciated. I myself have been unable so far to find a practical solution...

编辑#2:原来KB编号为Win8的,但有类似的修补程序对Win7的&放大器; Vista的KB 2756203.下,如果有人能使用它,发现在这里下载修补程序:的 http://thehotfixshare.net/board/index.php?autocom=downloads&showfile=15569 。我们测试了它,它确实解决了这个问题。如果我们发现绝对没有变通方法很快,我们会去的修补程序。

EDIT#2: The original KB number is for Win8 but there is similar Hotfix for Win7 & Vista under KB 2756203. If somebody can use it, found the hotfix for download here: http://thehotfixshare.net/board/index.php?autocom=downloads&showfile=15569. We tested it and it really fixes the problem. If we find definitely no workaround soon, we'll go with the hotfix.

编辑#3:备注为接受的解决方案提出了spajce 显然,呼吁展()的任意的文本菜单将说服原来的MenuStrip忘记它的焦点索赔。这可以以一种方式进行,使得虚设文本菜单甚至没有显示在屏幕上。我发现插入任何形式的构造函数下面的代码片段最短,最容易实现的方法:

EDIT#3: Remarks to the accepted solution proposed by spajce Obviously calling Show() on any ContextMenu will convince the original MenuStrip to forget about its claim on focus. This can be done in a way so that the dummy ContextMenu is not even shown on screen. I found the shortest and most easy to implement way to insert the following snippet in any Form's constructor:

public partial class Form1 : Form {
    public Form1() {
        InitializeComponent();

        using (var dummyMenu = new ContextMenuStrip()) {
            dummyMenu.Items.Add(new ToolStripMenuItem());
            dummyMenu.Show(Point.Empty);
        }
    }
}

于是打开清理ToolStripMenu系统的损坏状态的每个表。其中还不如把这个code在静态方法类似FormHelper.FixToolStripState()或将模板形式,它在OnCreateControl(...),并继承一切形式从(什么是什么,我们很幸运做的工作)。

So every Form that opens cleans up the corrupted state of the ToolStripMenu system. One might as well put this code in a static method like FormHelper.FixToolStripState() or put it in OnCreateControl(...) of a template Form and inherit all Forms from that (what is what we luckily do anyway).

推荐答案

这是我的解决办法:)

 static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            // Construct a MenuStrip with one item "Menu" with one dropdown-item "Popup"
            var mainMenu = new MenuStrip();
            var mainItem = new ToolStripMenuItem("Menu");
            mainItem.DropDownItems.Add(new ToolStripMenuItem("Popup", null, Popup));
            mainMenu.Items.Add(mainItem);

            // Create form with MenuStrip and Show
            var form1 = new Form();
            form1.Controls.Add(mainMenu);
            Application.Run(form1);
        }

        private static void Popup(object sender, EventArgs e)
        {
            // Create a form with a right click handler and show
            var form2 = new Form();
            var contextMenu = new ContextMenuStrip();
            contextMenu.Items.Add("Just an item...");
            var loc = form2.Location; //<---- get the location
            var p2 = new Point(loc.X, loc.Y); //<---- get the point of form
            form2.ContextMenuStrip = contextMenu;
            form2.ContextMenuStrip.Show(form2, p2); //<---- just add this code.
            form2.Show();
            // Problem: contextMenu.Show() will give focus to form1
        }
    }