2013-01-20 25 views
8

Possiedo un'applicazione MVVM Cross in esecuzione su Windows Phone 8 che ho recentemente trasferito utilizzando Librerie di classi portatili.Aggiornamento del thread UI dalla libreria di classi portatili

I modelli di vista si trovano all'interno della libreria di classi portatile e uno di essi espone una proprietà che abilita e disabilita un PerformanceProgressBar dal toolkit Silverlight for WP tramite associazione dati.

Quando l'utente preme un pulsante, RelayCommand avvia un processo in background che imposta la proprietà su true che dovrebbe abilitare la barra di avanzamento e l'elaborazione in background.

Prima di portarlo su un PCL, è stato possibile richiamare la modifica dal thread dell'interfaccia utente per garantire che la barra di avanzamento sia abilitata, ma l'oggetto Dispatcher non è disponibile in un PCL. Come posso aggirare questo?

Grazie

Dan

+1

io non sono del tutto sicuro nella vittoria-phone, ma si può usare il 'Application.Current.Dispatcher' per invocare l'aggiornamento? O sarebbe 'Deployment.Current.Dispatcher'? –

risposta

12

Se non si ha accesso al Dispatcher, si può semplicemente passare un delegato del metodo BeginInvoke alla classe:

public class YourViewModel 
{ 
    public YourViewModel(Action<Action> beginInvoke) 
    { 
     this.BeginInvoke = beginInvoke; 
    } 

    protected Action<Action> BeginInvoke { get; private set; } 

    private void SomeMethod() 
    { 
     this.BeginInvoke(() => DoSomething()); 
    } 
} 

Poi instanciate esso (da una classe che ha accesso al dispatcher):

var dispatcherDelegate = action => Dispatcher.BeginInvoke(action); 

var viewModel = new YourViewModel(dispatcherDelegate); 

Oppure puoi anche creare un wrapper attorno al tuo committente.

In primo luogo, definire un'interfaccia IDispatcher nella vostra libreria di classi portatile:

public interface IDispatcher 
{ 
    void BeginInvoke(Action action); 
} 

Poi, nel progetto che ha accesso al dispatcher, implementare l'interfaccia:

public class DispatcherWrapper : IDispatcher 
{ 
    public DispatcherWrapper(Dispatcher dispatcher) 
    { 
     this.Dispatcher = dispatcher; 
    } 

    protected Dispatcher Dispatcher { get; private set; } 

    public void BeginInvoke(Action action) 
    { 
     this.Dispatcher.BeginInvoke(action); 
    } 
} 

allora si può solo passare questo oggetto come istanza IDispatcher alla libreria di classi portatile.

+0

Questo è un approccio fattibile - ed è fondamentalmente esattamente ciò che fa MvvmCross - solo lo fa usando IoC a livello di installazione della piattaforma – Stuart

+0

Grazie - ha funzionato per me! –

+0

La vostra chiamata ... Ma, se si utilizza MvvmCross, probabilmente si troverà più facilmente dev usando 'InvokeOnMainThread (action)' - poiché ciò fornirà implementazioni predefinite su ogni piattaforma (e include cose come ' CheckAccess() 'chiama prima di ogni Invoke) – Stuart

15

Tutte le piattaforme MvvmCross richiedono che UI-azioni vengono schierate nuovamente al UI Discussione/appartamento - ma ogni piattaforma fa questo in modo diverso ....

Per ovviare a questo, MvvmCross fornisce un modo cross-platform per fare ciò - usando un oggetto iniettato IMvxViewDispatcherProvider.

Per esempio, su WindowsPhone IMvxViewDispatcherProvider è fornito in ultima analisi, da MvxMainThreadDispatcher in https://github.com/slodge/MvvmCross/blob/vnext/Cirrious/Cirrious.MvvmCross.WindowsPhone/Views/MvxMainThreadDispatcher.cs

Questo implementa la InvokeOnMainThread utilizzando:

private bool InvokeOrBeginInvoke(Action action) 
    { 
     if (_uiDispatcher.CheckAccess()) 
      action(); 
     else 
      _uiDispatcher.BeginInvoke(action); 

     return true; 
    } 

Per il codice in ViewModels:

  • tua ViewModel eredita da MvxViewModel
  • i MvxViewModel eredita da un MvxApplicationObject
  • i MvxApplicationObject eredita da un MvxNotifyPropertyChanged
  • l'oggetto MvxNotifyPropertyChanged eredita da un MvxMainThreadDispatchingObject

MvxMainThreadDispatchingObject è https://github.com/slodge/MvvmCross/blob/vnext/Cirrious/Cirrious.MvvmCross/ViewModels/MvxMainThreadDispatchingObject.cs

public abstract class MvxMainThreadDispatchingObject 
    : IMvxServiceConsumer<IMvxViewDispatcherProvider> 
{ 
    protected IMvxViewDispatcher ViewDispatcher 
    { 
     get { return this.GetService().Dispatcher; } 
    } 

    protected void InvokeOnMainThread(Action action) 
    { 
     if (ViewDispatcher != null) 
      ViewDispatcher.RequestMainThreadAction(action); 
    } 
} 

Quindi ... il tuo punto di vista Modello può chiamare InvokeOnMainThread(() => DoStuff());


Un altro punto da notare è che MvvmCross fa automaticamente conversioni thread dell'interfaccia utente per gli aggiornamenti di proprietà che vengono segnalati in un MvxViewModel (né in alcuna MvxNotifyPropertyChanged oggetto) attraverso i RaisePropertyChanged() metodi - vedi:

protected void RaisePropertyChanged(string whichProperty) 
    { 
     // check for subscription before going multithreaded 
     if (PropertyChanged == null) 
      return; 

     InvokeOnMainThread(
      () => 
       { 
        var handler = PropertyChanged; 

        if (handler != null) 
         handler(this, new PropertyChangedEventArgs(whichProperty)); 
       }); 
    } 

in https://github.com/slodge/MvvmCross/blob/vnext/Cirrious/Cirrious.MvvmCross/ViewModels/MvxNotifyPropertyChanged.cs


Questo il marshalling automatico delle chiamate RaisePropertyChanged() funziona bene per la maggior parte delle situazioni, ma può essere un po 'inefficiente se si aumentano molte proprietà modificate da un thread in background, si può verificare un elevato cambio di contesto dei thread. Non è qualcosa che è necessario essere a conoscenza di nella maggior parte del codice - ma se mai lo trovo è un problema, allora può aiutare a cambiare il codice come:

MyProperty1 = newValue1; 
MyProperty2 = newValue2; 
// ... 
MyProperty10 = newValue10; 

a:

InvokeOnMainThread(() => { 
     MyProperty1 = newValue1; 
     MyProperty2 = newValue2; 
     // ... 
     MyProperty10 = newValue10; 
}); 

Se mai utilizzate ObservableCollection, poi si ricorda che MvvmCross fa non effettuare una discussione smistamento per le INotifyPropertyChanged o INotifyCollectionChanged eventi generati da queste classi - quindi sta a voi come uno sviluppatore di marshall questi cambiamenti .

Il motivo: ObservableCollection esiste nelle basi di codice MS e Mono, quindi non è facile che MvvmCross possa modificare queste implementazioni esistenti.

1

Un'altra opzione che potrebbe essere più semplice è memorizzare un riferimento a SynchronizationContext.Current nel costruttore della classe. Quindi, in seguito, è possibile utilizzare _context.Post (() => ...) per richiamare nel contesto, ovvero il thread dell'interfaccia utente in WPF/WinRT/SL.

class MyViewModel 
{ 
    private readonly SynchronizationContext _context; 
    public MyViewModel() 
    { 
     _context = SynchronizationContext.Current. 
    } 

    private void MyCallbackOnAnotherThread() 
    { 
     _context.Post(() => UpdateTheUi()); 
    } 
} 
Problemi correlati