Aggiorna l’interfaccia utente dal thread in WinRT

Da quando l’anteprima del consumer di Windows 8 è stata rilasciata pochi giorni fa, sto lavorando al nuovo WinRT (per Metro Applications) in C # e ho portato la mia class IRC autoprodotta al nuovo threading e networking.

Il problema è: la mia class sta eseguendo un thread per ricevere messaggi dal server. Se ciò accade, il thread sta eseguendo un po ‘di analisi e quindi sta sparando un evento per informare l’applicazione su questo. La funzione sottoscritta quindi ‘dovrebbe’ aggiornare l’interfaccia utente (un blocco di testo).

Questo è il problema, il thread non può aggiornare l’interfaccia utente e il metodo di invoker che ha funzionato con .NET 4.0 non sembra più essere ansible. C’è una nuova soluzione per questo o anche un modo migliore per aggiornare l’interfaccia utente? Se provo ad aggiornare l’interfaccia utente dal sottoscrittore dell’evento otterrò questa Exception :

L’applicazione ha chiamato un’interfaccia che è stata sottoposta a marshalling per un thread diverso (Eccezione da HRESULT: 0x8001010E (RPC_E_WRONG_THREAD))

Il modo migliore per gestire questo in WinRT (e C # 5 in generale) è utilizzare asyncawait :

 private async void Button_Click(object sender, RoutedEventArgs e) { string text = await Task.Run(() => Compute()); this.TextBlock.Text = text; } 

Qui, il metodo Compute() verrà eseguito su un thread in background e al termine, il resto del metodo verrà eseguito sul thread dell’interfaccia utente. Nel frattempo, il thread dell’interfaccia utente è libero di fare tutto ciò di cui ha bisogno (come l’elaborazione di altri eventi).

Ma se non vuoi o non puoi usare async , puoi usare Dispatcher , in un modo simile (anche se diverso) come in WPF:

 private void Button_Click(object sender, RoutedEventArgs e) { Task.Run(() => Compute()); } private void Compute() { // perform computation here Dispatcher.Invoke(CoreDispatcherPriority.Normal, ShowText, this, resultString); } private void ShowText(object sender, InvokedHandlerArgs e) { this.TextBlock.Text = (string)e.Context; } 

Ecco un modo più semplice per farlo credo!

Prima acquisisci l’interfaccia utente SyncronizationContext con quanto segue:

  var UISyncContext = TaskScheduler.FromCurrentSynchronizationContext(); 

Esegui l’operazione di chiamata del server o qualsiasi altra operazione di thread in background necessaria:

  Task serverTask= Task.Run(()=> { /* DoWorkHere(); */} ); 

Quindi esegui l’operazione dell’interfaccia utente su UISyncContext che hai catturato nel primo passaggio:

  Task uiTask= serverTask.ContinueWith((t)=>{TextBlockName.Text="your value"; }, UISyncContext); 

IMO Penso che “ThreadPool” sia il percorso consigliato.

https://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh465290.aspx

 public static Task InvokeBackground(Func action) { var tcs = new TaskCompletionSource(); var unused = ThreadPool.RunAsync(async (obj) => { await action(); tcs.TrySetResult(true); }); return tcs.Task; }