我们最近升级到了VS 2012,同时还升级了.NET Framework 4.5。这引入了与WinForms MenuStrip控件有关的奇怪且令人讨厌的错误。在以.NET Framework 4.0为目标的应用程序中也会发生相同的错误,因为4.5的安装程序显然也会更新4.0的某些部分。因此,由于框架升级,一个完美工作的代码模式现在被破坏了。
问题描述:
我有一个带有MenuStrip的Form1。对于其中一个下拉项,事件处理程序将打开另一个Form2(不一定是 child ,而是另一个Form)。如果用户右键单击新的Form2或其子控件之一,并触发ContextMenuStrip的Show(),则原始Form1再次弹出到前台。
这独立于Form2中所有其他先前的UI Action 而发生。可以调整Form2的大小,移动,最小化,最大化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 Framework源代码,发现根本原因很可能出现在System.Windows.Forms.ToolStripManager中。在那里,Microsoft使用消息过滤器来跟踪窗口激活,该窗口激活在某种程度上不正确地用于MenuStrip。
同时,我还发现Microsoft已经在修复程序中解决了此问题(请参阅http://support.microsoft.com/kb/2769674),并希望这将在以后的.NET Framework 4.5更新中找到解决方法。
不幸的是,很难在所有客户端计算机上强制应用此修补程序。因此,仍将不胜感激可行的解决方法。到目前为止,我本人仍无法找到切实可行的解决方案。
编辑#2:原始KB号用于Win8,但在KB 2756203下有类似的Win7和Vista修补程序。如果有人可以使用,请在此处找到可下载的修补程序:http://thehotfixshare.net/board/index.php?autocom=downloads&showfile=15569。我们对其进行了测试,它确实解决了该问题。如果我们很快发现绝对没有解决方法,我们将使用此修复程序。
编辑#3:注释spajce 提出的已接受解决方案
显然,在任何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()之类的静态方法中,或者将其放在模板Form的OnCreateControl(...)中,并从中继承所有Forms(无论如何,我们很幸运地做到了)。
最佳答案
这是我的解决方案:)
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
}
}
关于c# - .NET 4.0/4.5 WinForms MenuStrip窃取焦点的奇怪错误,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/14058222/