Странная ошибка с .NET 4.0/4.5 WinForms MenuStrip Кража фокуса
Недавно мы обновились до версии VS 2012, которая сопровождается обновлением до .NET Framework 4.5. Это вводит странную и назойливую ошибку в связи с управлением WinForms MenuStrip. Такая же ошибка возникает и в приложениях, нацеленных на .NET Framework 4.0, поскольку 4.5-инсталлятор, очевидно, также обновляет части 4.0. Таким образом, совершенно рабочий шаблон кода теперь разбит только из-за обновления структуры.
Описание проблемы:
У меня есть Form1 с MenuStrip. Для одного из выпадающих элементов обработчик события открывает еще один Form2 (не обязательно дочерний элемент, а только другую форму). Если пользователь щелкает правой кнопкой мыши в новый Form2 или один из его дочерних элементов управления, и это вызывает Show() для ContextMenuStrip, тогда оригинальная Form1 снова появляется на переднем плане.
Это происходит независимо от всех предыдущих других действий пользовательского интерфейса в Form2. Можно изменять размер, перемещать, сворачивать, максимизировать Form2, переключаться между элементами управления, вводить текст и т.д. Как-то MenuStrip формы Form1, похоже, помнит, что это вызвало открытие 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, но есть аналогичное исправление для Win7 и Vista под KB 2756203. Если кто-то может его использовать, найдите исправление для загрузки здесь: http://thehotfixshare.net/board/index.php?autocom=downloads&showfile=15569. Мы протестировали его, и это действительно устраняет проблему. Если в ближайшее время мы обнаружим, что не будет обходного пути, мы перейдем к исправлению.
РЕДАКТИРОВАТЬ № 3: Замечания к принятому решению, предложенному spajce
Очевидно, что вызов Show() на любом ContextMenu убедит оригинальный MenuStrip забыть о своем требовании в фокусе. Это можно сделать так, чтобы фиктивный ContextMenu даже не отображался на экране. Я нашел самый короткий и самый простой способ реализовать следующий фрагмент в любом конструкторе формы:
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
using (var dummyMenu = new ContextMenuStrip()) {
dummyMenu.Items.Add(new ToolStripMenuItem());
dummyMenu.Show(Point.Empty);
}
}
}
Таким образом, каждая открываемая форма очищает поврежденное состояние системы ToolStripMenu. Можно также поставить этот код в статическом методе, например FormHelper.FixToolStripState(), или поместить его в OnCreateControl (...) формы шаблона и наследовать все формы из этого (что нам, к счастью, так или иначе).
Ответы
Ответ 1
это мое решение:)
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
}
}
Ответ 2
У меня тоже была эта проблема. Я не хотел менять код, потому что я чувствовал, что моя программа делает все правильно. Я нашел исправление на веб-сайте Microsoft:
http://support.microsoft.com/kb/2750147
Это действительно имеет значение, и мы только что установили его на 2 пользовательских компьютерах этим утром. Сегодня наш ИТ-специалист устанавливает его на всех компьютерах, когда он был протестирован и показан для работы.