. net 4.0/4.5 WinForms菜单条窃取焦点的奇怪Bug

本文关键字:焦点 Bug WinForms 菜单 net | 更新日期: 2023-09-27 17:49:45

我们最近升级到了VS 2012,同时也升级到了。net Framework 4.5。这引入了一个与WinForms MenuStrip控件有关的奇怪而讨厌的错误。同样的错误也出现在针对。net Framework 4.0的应用程序中,因为4.5的安装程序显然也更新了4.0的部分内容。因此,仅仅因为框架升级,一个完美工作的代码模式就被破坏了。

问题描述:
我有一个带有菜单条的Form1。对于其中一个下拉项,事件处理程序打开另一个Form2(不一定是子窗体,只是另一个Form)。如果用户右键单击新的Form2或它的一个子控件,这会触发ContextMenuStrip的Show(),那么原来的Form1会再次弹出到前台。
这独立于Form2中所有之前的其他UI操作。可以调整大小,移动,最小化,最大化Form2,在控件之间切换,输入文本等。不知何故,Form1的MenuStrip似乎记得它导致打开Form2并在第一次右键单击时抓住焦点。

我尝试了不同的方法,但到目前为止还无法找到一个解决方案。这个星座并不罕见,可能会影响周围的许多WinForms应用程序。因此,我决定把它贴在这里,因为一个可行的解决方案可能是普遍感兴趣的。我会很高兴如果有人知道一个变通或至少有一些线索给我。

我能够在下面的代码中提炼出它的本质。它应该在任何安装了。net 4.5的机器上都是可复制的,当目标分别为4.0和4.5时,它就会出现——但在3.5或更低的机器上就不会出现。

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框架的源代码,发现根本原因很可能是在System.Windows.Forms.ToolStripManager。在那里,微软正在使用一个消息过滤器来跟踪窗口激活,这在某种程度上是不正确的。
与此同时,我还发现微软已经在一个热修复程序中解决了这个问题(参见http://support.microsoft.com/kb/2769674),希望它能在未来的。net Framework 4.5更新中找到它的方式。

不幸的是,很难强制在所有客户端机器上应用此修补程序。因此,一个可行的解决方案将仍然是非常赞赏。到目前为止,我自己还没能找到一个切实可行的解决办法。

EDIT#2:原来的KB数是为Win8,但有类似的修复Win7 &在KB 2756203下的Vista。如果有人可以使用它,在这里找到下载的修复程序:http://thehotfixshare.net/board/index.php?autocom=downloads&showfile=15569。我们测试了它,它确实解决了问题。如果我们很快找不到解决办法,我们将使用热修复程序。

EDIT#3:对space
提出的已被接受的解决方案的注释显然,在any ContextMenu上调用Show()将说服原来的MenuStrip忘记它对焦点的声明。这可以通过一种方式完成,使虚拟ContextMenu甚至不显示在屏幕上。我发现了在任何Form的构造函数中插入以下代码片段的最短和最容易实现的方法:

public partial class Form1 : Form {
    public Form1() {
        InitializeComponent();
        using (var dummyMenu = new ContextMenuStrip()) {
            dummyMenu.Items.Add(new ToolStripMenuItem());
            dummyMenu.Show(Point.Empty);
        }
    }
}

所以每个打开的Form都会清理ToolStripMenu系统的损坏状态。一个人不妨把这段代码在一个静态的方法,如FormHelper.FixToolStripState()或把它放在OnCreateControl(…)的模板表单和继承所有的表单从(什么是我们幸运的是做什么)。

. net 4.0/4.5 WinForms菜单条窃取焦点的奇怪Bug

这是我的解决方案:)

 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
        }
    }

我也有这个问题。我不想修改代码,因为我觉得我的程序做得很好。我在微软网站上找到了修复方法:

http://support.microsoft.com/kb/2750147

它确实有作用,我们今天早上刚刚在两台用户电脑上安装了它。我们的IT人员今天正在把它安装到所有的电脑上,因为它已经经过测试并显示可以工作。