从便携式类库更新UI线程

Dan*_*ler 8 portable-class-library mvvmcross windows-phone-8

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

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

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

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

谢谢

Stu*_*art 15

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

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

例如,WindowsPhone IMvxViewDispatcherProvider最终由https://github.com/slodge/MvvmCross/blob/vnext/Cirrious/Cirrious.MvvmCross.WindowsPhone/Views/MvxMainThreadDispatcher.cs提供MvxMainThreadDispatcher.

这实现了InvokeOnMainThread使用:

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

        return true;
    }
Run Code Online (Sandbox Code Playgroud)

对于ViewModels中的代码:

  • 你的ViewModel继承权MvxViewModel
  • MvxViewModel从继承MvxApplicationObject
  • MvxApplicationObject从继承MvxNotifyPropertyChanged
  • MvxNotifyPropertyChanged对象继承自MvxMainThreadDispatchingObject

MvxMainThreadDispatchingObjecthttps://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);
    }
}
Run Code Online (Sandbox Code Playgroud)

所以...你的ViewModel可以调用 InvokeOnMainThread(() => DoStuff());


还有一点需要注意的是,MvvmCross会自动对属性更新进行UI线程转换,这些属性更新通过这些方法在MvxViewModel(或实际上在任何MvxNotifyPropertyChanged对象中)发出信号RaisePropertyChanged()- 请参阅:

    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));
                });
    }
Run Code Online (Sandbox Code Playgroud)

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


这种自动RaisePropertyChanged()调用调用在大多数情况下都很有效,但是如果从后台线程中提取大量已更改的属性,则可能效率有点 - 它可能会导致大量的线程上下文切换.在大多数代码中,您不需要注意这一点 - 但如果您确实发现它是一个问题,那么它可以帮助更改代码,如:

 MyProperty1 = newValue1;
 MyProperty2 = newValue2;
 // ...
 MyProperty10 = newValue10;
Run Code Online (Sandbox Code Playgroud)

至:

 InvokeOnMainThread(() => {
      MyProperty1 = newValue1;
      MyProperty2 = newValue2;
      // ...
      MyProperty10 = newValue10;
 });
Run Code Online (Sandbox Code Playgroud)

如果你曾经使用ObservableCollection,那么请注意,MvvmCross并没有为做任何线程编组INotifyPropertyChangedINotifyCollectionChanged由这些类触发的事件-所以它是由你作为一个开发者马歇尔这些变化.

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


Kev*_*sse 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());
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

var viewModel = new YourViewModel(dispatcherDelegate);
Run Code Online (Sandbox Code Playgroud)

或者您也可以在调度程序周围创建一个包装器.

首先,在可移植类库中定义IDispatcher接口:

public interface IDispatcher
{
    void BeginInvoke(Action action);
}
Run Code Online (Sandbox Code Playgroud)

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

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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