簡體   English   中英

.NET 4.0 / 4.5 WinForms的奇怪錯誤MenuStrip竊取焦點

[英]Strange Bug with .NET 4.0/4.5 WinForms MenuStrip Stealing Focus

我們最近升級到VS 2012,同時升級到.NET Framework 4.5。 這引入了與WinForms MenuStrip控件相關的奇怪且討厭的錯誤。 在針對.NET Framework 4.0的應用程序中也會出現同樣的錯誤,因為4.5的安裝程序顯然也會更新4.0的部分內容。 因此,由於框架升級,現在打破了完美的代碼模式。

問題描述:
我有一個帶有MenuStrip的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 Framework源代碼,發現根本原因很可能在System.Windows.Forms.ToolStripManager中。 在那里,微軟正在使用消息過濾器來跟蹤窗口激活,這在某種程度上是錯誤地為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(...)中,並從那里繼承所有的Form(無論如何我們幸運地做了什么)。

這是我的解決方案:)

 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

它確實有所作為,我們今天早上只在2台用戶計算機上安裝了它。 我們的IT人員現在正在所有計算機上安裝它,因為它已經過測試並顯示出工作。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM