2010-02-05 2 views
6

Ho un programma con una mappa Geospace incorporata in esso. La gestione degli eventi per la mappa viene gestita su un thread separato per mantenere la mappa reattiva (ad esempio, gli eventi che si attivano quando si fa clic sulla mappa).C#, WPF, chiama automaticamente Dispatcher.Invoke quando necessario?

Il problema che sto avendo è quando la mappa attiva un evento, il mio programma ha bisogno di aggiornare alcune cose nel suo gui, e anche richiamare sulla mappa per gestire l'inserimento delle immagini sulla mappa.

Ho provato a racchiudere l'intero metodo del gestore di eventi in this.Dispatcher.Invoke, che mi riporta al thread dell'interfaccia utente principale. Funziona perfettamente per l'aggiornamento della GUI, ma quando richiamo la mappa, sono ancora sul thread dell'interfaccia utente che può causare alcuni problemi nella mappa.

Fondamentalmente, per fare questo lavoro, dovrò eseguire dispatcher.invoke ogni volta che voglio cambiare un controllo sul mio gui. C'è un modo in cui posso farlo automaticamente senza includere ogni chiamata in dispatcher.invoke? Spero che tutto ciò abbia un senso.

Ecco qualche esempio di codice per l'evento di cui sto parlando ..

private void Map_OnMapClicked(object sender, MapClickedEventArgs e) 
    { 
     this.Dispatcher.Invoke(DispatcherPriority.Normal, (Action)(() => 
     { 
      // Do something to update my gui 
     })); 

     Map.DoSomethingInTheMap(); 

     this.Dispatcher.Invoke(DispatcherPriority.Normal, (Action)(() => 
     { 
      // Do something to update my gui 
     })); 


     //etc etc etc 
    } 

risposta

6

Se avete bisogno di tenere ogni rispettiva operazione nel proprio contesto di sincronizzazione, questo è purtroppo l'approccio migliore. Avrai bisogno di richiamare usando il Dispatcher ogni volta che aggiorni la tua GUI.

Qui ci sono un paio di suggerimenti per rendere questo più facile:

  1. Prova a partita le operazioni di GUI. Oltre a richiedere meno codice (tramite meno chiamate di richiamo), otterrai prestazioni migliori. Ogni chiamata Dispatcher.Invoke comporta una notevole quantità di overhead, poiché pubblica un messaggio nella coda dei messaggi del Dispatcher che deve essere elaborata.

  2. Considerare l'utilizzo di Dispatcher.BeginInvoke per evitare il blocco, a meno che non sia davvero necessario attendere.

  3. Se è possibile utilizzare la Libreria parallela attività da .NET 4 (o il backport a 3.5sp1 in Rx Framework), è possibile prendere in considerazione la possibilità di rielaborare per utilizzare Task instances synchronized to the GUI thread. Creando un TaskScheduler utilizzando FromCurrentSynchronizationContext, è possibile pianificare le attività da eseguire sulla GUI più facilmente rispetto alle chiamate Invoke di Dispatcher. Questo può anche darti un controllo sul batching, dal momento che puoi programmarli e bloccare/attendere quando necessario, molto facilmente.

2

Si potrebbe usare qualcosa come PostSharp o cercare di condensare i tuoi aggiornamenti dell'interfaccia utente al singolo metodo chiamate in cui si invoca una volta e fare un sacco. O anche un modello come questo (è Winforms ma l'idea è la stessa):

private void UpdateToolStripItemText(ToolStripItem toolStripItem, string text) 
{ 
    if (InvokeRequired) 
    { 
     Invoke(new UpdateToolStripItemTextDelegate(UpdateToolStripItemText), new object[] { toolStripItem, text }); 
    } 
    else 
    { 
     if (text != null) 
     { 
      toolStripItem.Text = text; 
     } 
    } 
}