从便携式类库更新UI线程

时间:2013-01-20 17:48:03

标签: windows-phone-8 mvvmcross portable-class-library

我有一个在Windows Phone 8上运行的MVVM Cross应用程序,我最近移植到了使用可移植类库。

视图模型位于可移植类库中,其中一个公开了一个属性,该属性通过数据绑定从Silverlight for WP工具包启用和禁用PerformanceProgressBar。

当用户按下按钮时,RelayCommand启动后台进程,该进程将属性设置为true,这将启用进度条并进行后台处理。

在将其移植到PCL之前,我能够从UI线程调用更改以确保启用了进度条,但是在PCL中无法使用Dispatcher对象。我该如何解决这个问题?

由于

3 个答案:

答案 0 :(得分:15)

所有MvvmCross平台都要求将UI操作编组回UI线程/公寓 - 但每个平台都以不同方式执行....

要解决这个问题,MvvmCross提供了一种跨平台的方法 - 使用IMvxViewDispatcherProvider注入的对象。

例如,在Windows Phone [{1}}上最终由https://github.com/slodge/MvvmCross/blob/vnext/Cirrious/Cirrious.MvvmCross.WindowsPhone/Views/MvxMainThreadDispatcher.cs中的IMvxViewDispatcherProvider提供

这实现了MvxMainThreadDispatcher使用:

InvokeOnMainThread

对于ViewModels中的代码:

  • 您的 private bool InvokeOrBeginInvoke(Action action) { if (_uiDispatcher.CheckAccess()) action(); else _uiDispatcher.BeginInvoke(action); return true; } 继承自ViewModel
  • MvxViewModel继承自MvxViewModel
  • MvxApplicationObject继承自MvxApplicationObject
  • MvxNotifyPropertyChanged对象继承自MvxNotifyPropertyChanged

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

MvxMainThreadDispatchingObject

所以......你的ViewModel可以只调用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); } }


另外需要注意的一点是,MvvmCross会自动对属性更新进行UI线程转换,这些属性更新通过InvokeOnMainThread(() => DoStuff());方法在MvxViewModel(或实际上在任何MvxNotifyPropertyChanged对象中)发出信号 - 见:

RaisePropertyChanged()
https://github.com/slodge/MvvmCross/blob/vnext/Cirrious/Cirrious.MvvmCross/ViewModels/MvxNotifyPropertyChanged.cs

中的


这种 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)); }); } 调用的自动编组适用于大多数情况,但如果从后台线程中提取大量已更改的属性,则可能效率有点 - 它可能会导致大量线程上下文切换。在大多数代码中,您不需要注意这一点 - 但如果您确实发现它是一个问题,那么它可以帮助更改代码,如:

RaisePropertyChanged()

为:

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

如果您曾使用 InvokeOnMainThread(() => { MyProperty1 = newValue1; MyProperty2 = newValue2; // ... MyProperty10 = newValue10; }); ,请注意MvvmCross 对这些类触发的ObservableCollectionINotifyPropertyChanged事件执行任何线程编组操作 - 因此,作为开发人员,您可以自行决定这些变化。

原因:INotifyCollectionChanged存在于MS和Mono代码库中 - 因此MvvmCross没有简单的方法可以更改这些现有的实现。

答案 1 :(得分:12)

如果您无权访问Dispatcher,则只需将BeginInvoke方法的委托传递给您的类:

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

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

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

然后实例化(来自可以访问调度程序的类):

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

var viewModel = new YourViewModel(dispatcherDelegate);

或者您也可以在调度员周围创建一个包装器。

首先,在便携式类库中定义一个IDispatcher接口:

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

然后,在有权访问调度程序的项目中,实现接口:

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

然后,您可以将此对象作为IDispatcher实例传递给可移植类库。

答案 2 :(得分:1)

另一个更容易的选择是在类的构造函数中存储对SynchronizationContext.Current的引用。然后,稍后,您可以使用_context.Post(()=&gt; ...)来调用上下文 - 这是WPF / WinRT / SL中的UI线程。

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

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