2009-10-29 12 views
60

Ho iniziato di recente a programmare in WPF e ho riscontrato il seguente problema. Non capisco come utilizzare il metodo Dispatcher.Invoke(). Ho esperienza in threading e ho fatto un paio di semplici programmi Windows Form dove ho appena usato ilModifica i controlli WPF da un thread non principale utilizzando Dispatcher.Invoke

Control.CheckForIllegalCrossThreadCalls = false; 

Sì, lo so che è piuttosto scadente, ma questi erano semplici applicazioni di monitoraggio.

Il fatto è che ora sto facendo un'applicazione WPF che recupera i dati in background, avvio un nuovo thread per effettuare la chiamata per recuperare i dati (da un server web), ora voglio visualizzarli sul mio WPF modulo. Il fatto è che non posso impostare alcun controllo da questo thread. Neanche un'etichetta o niente. Come può essere risolto?

commenti La risposta:
@Jalfp:
Quindi io uso questo metodo Dispatcher nel 'nuovo battistrada' quando ricevo i dati? O dovrei fare in modo che un lavoratore in background recuperi i dati, li metta in un campo e inizi un nuovo thread che aspetta che questo campo venga riempito e chiama il dispatcher per mostrare i dati recuperati nei controlli?

risposta

145

La prima cosa è capire che, il Dispatcher non è progettato per eseguire operazioni di blocco lunghe (come il recupero di dati da un WebServer ...). È possibile utilizzare il Dispatcher quando si desidera eseguire un'operazione che verrà eseguita sul thread dell'interfaccia utente (ad esempio, l'aggiornamento del valore di una barra di avanzamento).

Che cosa è possibile fare è recuperare i dati in un worker in background e utilizzare il metodo ReportProgress per propagare le modifiche nel thread dell'interfaccia utente.

Se avete veramente bisogno di utilizzare direttamente il Dispatcher, è abbastanza semplice:

Application.Current.Dispatcher.BeginInvoke(
    DispatcherPriority.Background, 
    new Action(() => this.progressBar.Value = 50)); 
+19

È possibile liberarsi della 'nuova azione (' parte, e semplicemente usare un'espressione lambda: DispatcherPriority.Background,() => = 50 this.progressBar.Value – jrista

+0

Sì Non so perché ho messo un Azione qui: p – japf

+0

Quindi, quando ottengo i dati, utilizzo questo metodo di Dispatcher nel "nuovo battistrada" oppure devo fare in modo che un lavoratore in background recuperi i dati, li metta in un campo e inizi un nuovo thread che aspetta finché questo campo non è riempito e chiamare il dispatcher per mostrare i dati recuperati nei controlli? –

19

japf ha risposto correttamente. Nel caso in cui si guardi alle azioni su più righe, è possibile scrivere come di seguito.

Application.Current.Dispatcher.BeginInvoke(
    DispatcherPriority.Background, 
    new Action(() => { 
    this.progressBar.Value = 50; 
    })); 

Informazioni per gli altri utenti che vogliono sapere sulle prestazioni:

Se la vostra necessità di codice da scrivere per alte prestazioni, è possibile verificare se l'invoke è richiesto utilizzando CheckAccess bandiera.

if(Application.Current.Dispatcher.CheckAccess()) 
{ 
    this.progressBar.Value = 50; 
} 
else 
{ 
    Application.Current.Dispatcher.BeginInvoke(
     DispatcherPriority.Background, 
     new Action(() => { 
     this.progressBar.Value = 50; 
     })); 
} 

Si noti che il metodo CheckAccess() è nascosto da Visual Studio 2015 quindi basta scriverlo senza aspettarsi intellisense per mostrare in su. Si noti che CheckAccess ha un sovraccarico sulle prestazioni (sovraccarico in pochi nanosecondi). È solo meglio quando vuoi salvare quel microsecondo richiesto per eseguire il "richiamo" ad ogni costo. Inoltre, c'è sempre un'opzione per creare due metodi (on con invoke, e altri without) quando il metodo di chiamata è sicuro se è in UI Thread o no. È raro che rari casi in cui si dovrebbe guardare questo aspetto del dispatcher.

Problemi correlati