13

最近、.NET Framework 4.5 へのアップグレードを伴う VS 2012 にアップグレードしました。これにより、WinForms MenuStrip コントロールに関連して奇妙で厄介なバグが発生します。.NET Framework 4.0 を対象とするアプリケーションでも同じバグが発生します。これは、4.5 のインストーラーが明らかに 4.0 の一部も更新するためです。したがって、フレームワークのアップグレードが原因で、完全に機能するコード パターンが壊れてしまいます。

問題の説明:
MenuStrip を持つ Form1 があります。ドロップダウン項目の 1 つで、イベント ハンドラーは別の Form2 (必ずしも子ではなく、別の Form) を開きます。ユーザーが新しい Form2 またはその子コントロールの 1 つを右クリックすると、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 にある可能性が非常に高いことがわかりました。そこでは、Microsoft がメッセージ フィルターを使用してウィンドウのアクティブ化を追跡していますが、これは MenuStrip に対して何らかの方法で正しく実装されていません。
その間、Microsoft が既にホットフィックスでこの問題に対処していることもわかりました ( http://support.microsoft.com/kb/2769674を参照)。うまくいけば、これが .NET Framework 4.5 の将来の更新に反映されることを願っています。

残念ながら、この修正プログラムをすべてのクライアント マシンに適用することは困難です。したがって、実行可能な回避策が引き続き大歓迎です。私自身、これまでのところ実用的な解決策を見つけることができませんでした...

EDIT#2:元の KB 番号はWin8用ですが、KB 2756203 の下に Win7 および Vista 用の同様のホットフィックスがあります。?autocom=downloads&showfile=15569 . テストしたところ、実際に問題が修正されました。回避策がすぐに見つからない場合は、修正プログラムを使用します。

EDIT#3: spajce によって提案された受け入れられた解決策へのコメント明らかに、任意の
ContextMenu で Show() を呼び出すと、元の 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() のような静的メソッドに入れるか、テンプレート Form の OnCreateControl(...) に入れ、そこからすべての Forms を継承することもできます (幸運なことに、これはとにかく行います)。

4

2 に答える 2

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
        }
    }
于 2012-12-28T14:20:18.550 に答える
0

私もこの問題を抱えていました。プログラムが正しく動作していると感じたので、コードを変更したくありませんでした。MicrosoftのWebサイトで修正を見つけました:

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

それは違いを生み、今朝2台のユーザーコンピューターにインストールしました. 私たちの IT 担当者は、テストされて動作することが示されているため、今日すべてのコンピューターにインストールしています。

于 2014-03-27T14:22:03.473 に答える