Il mio modulo non viene visualizzato correttamente quando viene avviato da un altro thread

Ecco la situazione: sto sviluppando una semplice applicazione con la seguente struttura:

  • FormMain (punto di avvio)
  • FormNotification
  • CompleFunctions

Destra?

Bene, in FormMain ho la seguente funzione:

private void DoItInNewThread(ParameterizedThreadStart pParameterizedThreadStart, object pParameters, ThreadPriority pThreadPriority) { Thread oThread = new Thread(pParameterizedThreadStart); oThread.CurrentUICulture = Settings.Instance.Language; oThread.IsBackground = true; oThread.Priority = pThreadPriority; oThread.Name = "μRemote: Background operation"; oThread.Start(pParameters); } 

Quindi, ogni volta che ho bisogno di chiamare un metodo che richiede tempo situato su ComplexFunctions faccio quanto segue:

 // This is FormMain.cs string strSomeParameter = "lala"; DoItInNewThread(new ParameterizedThreadStart(ComplexFunctions.DoSomething), strSomeParameter, ThreadPriority.Normal); 

L’altra class, FormNotification, è una forma che visualizza alcune informazioni del processo all’utente. Questa FormNotification può essere chiamata da FormMain o ComplexFunctions. Esempio:

 // This is ComplexFunctions.cs public void DoSomething(string pSomeParameter) { // Imagine some time consuming task FormNotification formNotif = new FormNotification(); formNotif.Notify(); } 

FormNotify ha un timer, quindi, dopo 10 secondi chiude il modulo. Non sto usando formNotif.ShowDialog perché non voglio concentrarmi su questo modulo. Puoi controllare questo link per vedere cosa sto facendo in Notify.

Ok, ecco il problema: quando chiamo FormNotify da ComplexFunction che viene chiamato da un altro Thread in FormMain … questo FormNotify scompare dopo pochi millisecondi. È lo stesso effetto che quando fai qualcosa del genere:

 using(FormSomething formSomething = new FormSomething) { formSomething.Show(); } 

Come può evitare questo?

Queste sono le possibili soluzioni che non voglio usare:

  • Utilizzo di Thread.Sleep (10000) in FormNotify
  • Utilizzare FormNotif.ShowDialog ()

Questo è uno scenario semplificato (FormNotify fa altre cose di fantasia che rimangono solo per 10 secondi, ma sono irrilevanti per vedere il problema).

Grazie per il tuo tempo!!! E per favore, scusa il mio inglese.

Quasi tutte le librerie della GUI sono progettate per consentire solo le chiamate che modificano la GUI da effettuare in un singolo thread designato a tale scopo (chiamato il thread dell’interfaccia utente). Se si è in un’altra discussione, è necessario organizzare la chiamata per modificare la GUI da inserire nel thread dell’interfaccia utente. In .NET, il modo per farlo è chiamare Invoke (sincrono) o BeginInvoke (asincrono). L’equivalente chiamata Java Swing è invokeLater () – ci sono funzioni simili in quasi tutte le librerie della GUI.

Esiste qualcosa chiamata affinità di thread. Esistono due thread in un’applicazione WinForm, una per il rendering e una per la gestione dell’interfaccia utente. Trattate solo con thread dell’interfaccia utente. Il thread di rendering rimane nascosto – viene eseguito in background. Gli unici oggetti creati sul thread dell’interfaccia utente possono manipolare l’interfaccia utente, ovvero gli oggetti hanno affinità di thread con il thread dell’interfaccia utente.

Poiché, stai cercando di aggiornare l’interfaccia utente (mostra una notifica) da un thread diverso rispetto al thread dell’interfaccia utente. Quindi, nel tuo thread di lavoro, definisci un delegato e fai in modo che FormMain ascolti questo evento. Nel gestore di eventi (definire in FormMain) scrivere codice per mostrare FormNotify.

Spara l’evento dal thread di lavoro quando vuoi mostrare la notifica.

Quando un thread diverso dal thread di creazione di un controllo tenta di accedere a uno dei metodi o proprietà di quel controllo, spesso porta a risultati imprevedibili. Un’attività thread non valida comune è una chiamata sul thread errato che accede alla proprietà Handle del controllo. Impostare CheckForIllegalCrossThreadCalls su true per trovare e diagnosticare più facilmente questa attività thread durante il debug. Si noti che le chiamate cross-thread illegali generano sempre un’eccezione quando un’applicazione viene avviata all’esterno del debugger.

Nota: l’impostazione di CheckForIllegalCrossThreadCalls su ture dovrebbe essere eseguita SOLO nelle SITUAZIONI DI DEBUGGIN. Si verificheranno risultati imprevedibili e finirai per cercare di inseguire i bug che si avranno a trovare difficilmente.

Non sei autorizzato a effettuare chiamate WinForm da altri thread. Guarda BeginInvoke nel modulo: puoi chiamare un delegato per mostrare il modulo dal thread dell’interfaccia utente.

Modifica: dai commenti (non impostare CheckForIllegalCrossThreadCalls su false).

Ulteriori informazioni Quasi tutte le librerie della GUI sono progettate per consentire solo le chiamate che modificano la GUI da realizzare in un singolo thread designato a tale scopo (chiamato il thread dell’interfaccia utente). Se si è in un’altra discussione, è necessario organizzare la chiamata per modificare la GUI da inserire nel thread dell’interfaccia utente. In .NET, il modo per farlo è chiamare Invoke (sincrono) o BeginInvoke (asincrono). L’equivalente chiamata Java Swing è invokeLater () – ci sono funzioni simili in quasi tutte le librerie della GUI.

Esiste qualcosa chiamata affinità di thread. Esistono due thread in un’applicazione WinForm, una per il rendering e una per la gestione dell’interfaccia utente. Trattate solo con thread dell’interfaccia utente. Il thread di rendering rimane nascosto – viene eseguito in background. Gli unici oggetti creati sul thread dell’interfaccia utente possono manipolare l’interfaccia utente, ovvero gli oggetti hanno affinità di thread con il thread dell’interfaccia utente.

Poiché, stai cercando di aggiornare l’interfaccia utente (mostra una notifica) da un thread diverso rispetto al thread dell’interfaccia utente. Quindi, nel tuo thread di lavoro, definisci un delegato e fai in modo che FormMain ascolti questo evento. Nel gestore di eventi (definire in FormMain) scrivere codice per mostrare FormNotify.

Spara l’evento dal thread di lavoro quando vuoi mostrare la notifica.

Supponendo che tu abbia un pulsante nel modulo e desideri aprire un altro modulo Form1 quando l’utente fa clic su quel pulsante

  private void button1_Click(object sender, EventArgs e) { Thread t = new Thread(new ThreadStart(this.ShowForm1)); t.Start(); } 

Tutto quello che devi fare è controllare la proprietà InvokeRequired e se sì chiama il metodo Invoke del tuo modulo passando delegato ShowForm1, che finirà in chiamata ricorsiva dove InvokeRequired sarà falso

  delegate void Func(); private void ShowForm1() { if (this.InvokeRequired) { Func f = new Func(ShowForm1); this.Invoke(f); } else { Form1 form1 = new Form1(); form1.Show(); } } 

Utilizza la chiamata all’API SetWindowPos per assicurarti che il tuo modulo di notifica sia la finestra più in alto. Questo post spiega come:

http://www.pinvoke.net/default.aspx/user32/SetWindowPos.html