Updating the user interface stream from a portable class library

I have an MVVM Cross application running on Windows Phone 8 that I recently ported to Portable Class libraries.

Presentation models are in a portable class library, and one of them provides a property that enables and disables the PerformanceProgressBar from Silverlight for WP through data binding.

When the user clicks the button, RelayCommand starts the background process, which sets the true property, which should activate the progress bar and performs background processing.

Before I put it in the PCL, I managed to trigger a change from the user interface thread to make sure that the progress bar was on, but the Dispatcher was not available in the PCL. How can I get around this?

thanks

Dan

+8
portable-class-library windows-phone-8 mvvmcross
source share
3 answers

If you do not have access to the dispatcher, you can simply pass the delegate of the BeginInvoke method to your class:

public class YourViewModel { public YourViewModel(Action<Action> beginInvoke) { this.BeginInvoke = beginInvoke; } protected Action<Action> BeginInvoke { get; private set; } private void SomeMethod() { this.BeginInvoke(() => DoSomething()); } } 

Then install it (from the class that has access to the dispatcher):

 var dispatcherDelegate = action => Dispatcher.BeginInvoke(action); var viewModel = new YourViewModel(dispatcherDelegate); 

Or you can also create a wrapper around the dispatcher.

First, define the IDispatcher interface in your portable class library:

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

Then, in the project that has access to the dispatcher, we implement the interface:

 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); } } 

Then you can simply pass this object as an instance of IDispatcher to your portable class library.

+12
source share

On all MvvmCross platforms, it is required that user interfaces reconnect to the interface thread / apartment, but each platform does it differently ....

To get around this, MvvmCross provides a cross-platform way to do this - using the IMvxViewDispatcherProvider object.

For example, in WindowsPhone, IMvxViewDispatcherProvider provides the ultimate MvxMainThreadDispatcher at https://github.com/slodge/MvvmCross/blob/vnext/Cirrious/Cirrious.MvvmCross.WindowsPhone/Views/MvxMainThreadDispatcher.

This implements InvokeOnMainThread using:

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

For code in ViewModels:

  • your ViewModel inherits from MvxViewModel
  • MvxViewModel inherits from MvxApplicationObject
  • MvxApplicationObject inherits from MvxNotifyPropertyChanged
  • the MvxNotifyPropertyChanged object inherits from 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); } } 

So ... your ViewModel can just call InvokeOnMainThread(() => DoStuff());


Another point that should be noted is that MvvmCross automatically performs user interface thread conversions for property updates that are passed to the MvxViewModel (or even any MvxNotifyPropertyChanged object) using the RaisePropertyChanged() methods - see:

  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)); }); } 

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


This automatic sorting of RaisePropertyChanged() calls works well in most situations, but it can be a little inefficient if you raise a lot of changed properties from a background thread - this can lead to a lot of context switching of the stream. This is not something you need to know about in most of your codes, but if you ever find it to be a problem, it can help change the code, for example:

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

in

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

If you have ever used an ObservableCollection , then note that MvvmCross does not sort the threads for the INotifyPropertyChanged or INotifyCollectionChanged events fired by these classes, so for you as a developer to change these changes.

Reason: ObservableCollection exists in the MS and Mono base codes, so there is no easy way that MvvmCross can modify these existing implementations.

+15
source share

Another option, which might be simpler, is to keep a reference to SynchronizationContext.Current in your class constructor. Then, later, you can use _context.Post (() => ...) to call in context - this is the user interface thread in WPF / WinRT / SL.

 class MyViewModel { private readonly SynchronizationContext _context; public MyViewModel() { _context = SynchronizationContext.Current. } private void MyCallbackOnAnotherThread() { _context.Post(() => UpdateTheUi()); } } 
+1
source share

All Articles