2012-12-27 18 views
13

Abbiamo recentemente aggiornato a VS 2012, che è accompagnato da un aggiornamento a .NET Framework 4.5. Questo introduce un bug strano e fastidioso in connessione con il controllo MenuStrip di WinForms. Lo stesso bug si verifica anche nelle applicazioni che mirano a .NET Framework 4.0 come installer 4.5, ovviamente aggiorna anche parti di 4.0. Pertanto, un modello di codice perfettamente funzionante è ora rotto solo a causa dell'upgrade del framework.Strano errore con .NET 4.0/4.5 WinForms MenuStrip Stealing Focus

Descrizione del problema:
Ho un Form1 con un MenuStrip. Per uno degli elementi a discesa, il gestore eventi apre un altro Form2 (non necessariamente un bambino, solo un altro Form). Se l'utente fa clic con il pulsante destro del mouse sul nuovo Form2 o su uno dei relativi controlli figlio e attiva uno Show() di un ContextMenuStrip, il Form1 originale viene nuovamente visualizzato in primo piano.
Ciò accade indipendentemente da tutte le altre precedenti azioni dell'interfaccia utente in Form2. Uno può ridimensionare, spostare, minimizzare, ingrandire Form2, passare tra i controlli, immettere il testo, ecc. In qualche modo, MenuStrip di Form1 sembra ricordare che ha causato l'apertura di Form2 e che si concentra sul primo clic con il pulsante destro del mouse.

Stavo sperimentando approcci diversi ma non sono riuscito a trovare una soluzione alternativa finora. La costellazione non è rara e potrebbe interessare molte applicazioni WinForm in giro. Pertanto ho deciso di postarlo qui poiché una soluzione praticabile potrebbe essere di interesse generale. Sarei molto felice se qualcuno conosce una soluzione alternativa o almeno qualche indizio per me.

Sono stato in grado di distillare la sua essenza nel seguente codice. Dovrebbe essere riproducibile su qualsiasi macchina con .NET 4.5 installato e si verifica quando si punta su 4.0 e 4.5 ciascuno - ma non su 3.5 o inferiore.

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 
     } 
    } 
} 

EDIT: abbiamo trascorso qualche tempo passo del codice sorgente NET Framework e trovarono la causa principale di essere molto probabile nel System.Windows.Forms.ToolStripManager. Lì, Microsoft sta usando un filtro messaggi per tenere traccia dell'attivazione della finestra, che in qualche modo è stata implementata in modo errato per MenuStrip.
Nel frattempo ho anche scoperto che Microsoft ha già risolto questo problema in una correzione (vedere http://support.microsoft.com/kb/2769674) e si spera che questo troverà la sua strada in un futuro aggiornamento di .NET Framework 4.5.

Sfortunatamente è difficile applicare questo aggiornamento rapido da applicare su tutte le macchine client. Pertanto, una valida soluzione alternativa sarebbe ancora molto apprezzata. Io stesso sono stato in grado finora di trovare una soluzione pratica ...

EDIT # 2: Il numero KB originale è per Win8 ma c'è Hotfix simile per Win7 & Vista sotto KB 2756203. Se qualcuno può usarlo , trovato l'aggiornamento rapido per il download qui: http://thehotfixshare.net/board/index.php?autocom=downloads&showfile=15569. L'abbiamo testato e risolve davvero il problema. Se non troviamo alcuna soluzione al problema, ci andremo con l'aggiornamento rapido.

EDIT # 3: Osservazioni alla soluzione accettata proposti da spajce
Ovviamente chiamando Show() su qualsiasi ContextMenu convincerà l'originale MenuStrip a dimenticare la sua pretesa sul fuoco. Questo può essere fatto in modo tale che il fittizio ContextMenu non venga nemmeno visualizzato sullo schermo. Ho trovato il modo più breve e più facile da implementare per inserire il seguente frammento nel costruttore di qualsiasi forma:

public partial class Form1 : Form { 
    public Form1() { 
     InitializeComponent(); 

     using (var dummyMenu = new ContextMenuStrip()) { 
      dummyMenu.Items.Add(new ToolStripMenuItem()); 
      dummyMenu.Show(Point.Empty); 
     } 
    } 
} 

Così ogni modulo che si apre pulisce lo stato corrotto del sistema ToolStripMenu. Si potrebbe anche mettere questo codice in un metodo statico come FormHelper.FixToolStripState() o metterlo in OnCreateControl (...) di un modello Form ed ereditare tutti i Form da quello (cosa è ciò che fortunatamente facciamo comunque).

+3

Repro molto bello. Dubito seriamente che tu possa ottenere una risposta, le lezioni sul toolstrip sono un grande dolore. Porterei questo al supporto Microsoft. –

+0

Guardando la descrizione dell'hotfix, è il problema esattamente opposto. Non rimuove il filtro messaggi abbastanza velocemente. Basta fare clic con il tasto sinistro sul modulo per vedere il problema. C'è del codice nel gestore della striscia che dovrebbe rimuovere il filtro quando viene attivata un'altra finestra, questo non sta succedendo. Speriamo che questo hotfix faccia presto un aggiornamento, questo è un bug piuttosto grossolano. –

+0

La causa principale è difficile da affrontare poiché le diverse parti che contribuiscono sono molto distribuite nel codice sorgente. Dal momento che non ho visto nessun modo per sistemare il codice quadro, mi sono fermato a un certo punto. Sono rimasto sorpreso dal fatto che l'implementazione sottostante sia piuttosto sporca (secondo me) ma non mi sorprende che questo codice sia molto incline a tali bug. – markus987

risposta

1

questa è la mia soluzione :)

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 
     } 
    } 
+0

Stranamente, questo è davvero il trucco! Ho aggiornato la mia domanda di cui sopra per dargli più generalità (il nostro dovrei aver modificato la tua risposta direttamente?). Solo una piccola osservazione: poiché Point è una struttura, viene comunque copiata, quindi non è necessario loc/p2. – markus987

+0

Ammetto che sono solo un principiante, quindi il trucco che ho dato è solo il mio suggerimento di risolvere qualche piccolo bug che abbiamo riscontrato quindi il mio punto è che "nella programmazione ci sono sempre codici puliti per risolvere il problema e lo sporco è la nostra ultima arma! Ho letto la tua domanda aggiornata e questo è fantastico, potrebbe essere il modo migliore :) – spajce

+0

Per essere onesto, non credevo in primo luogo che la tua soluzione sarebbe stata così efficace. Ho sperimentato personalmente altre chiamate di Show() e ho scoperto che chiamare Show() due volte consecutive risolve il problema, ma non ho visto come ciò potrebbe essere pratico, considerando che abbiamo un enorme base di codice con centinaia di menu e il contextmenus. La tua soluzione proposta poi mi ha dato le giuste indicazioni. – markus987

0

ho avuto anche questo problema. Non volevo cambiare codice, perché sentivo che il mio programma stava facendo bene. Ho trovato la soluzione qui sul sito web Microsofts:

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

Si fa la differenza e abbiamo appena installato sul computer degli utenti 2 questa mattina. Il nostro tecnico IT lo sta installando su tutti i computer oggi, che è stato testato e dimostrato funzionare.