Подтвердить что ты не робот

Странная ошибка с .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 (...) формы шаблона и наследовать все формы из этого (что нам, к счастью, так или иначе).

4b9b3361

Ответ 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 пользовательских компьютерах этим утром. Сегодня наш ИТ-специалист устанавливает его на всех компьютерах, когда он был протестирован и показан для работы.