8

Windows Phone 8 で実行されている MVVM Cross アプリケーションを使用しており、これを最近、Portable Class Libraries を使用するように移植しました。

ビュー モデルはポータブル クラス ライブラリ内にあり、そのうちの 1 つは、データ バインディングを通じて Silverlight for WP ツールキットから PerformanceProgressBar を有効または無効にするプロパティを公開します。

ユーザーがボタンを押すと、RelayCommand がバックグラウンド プロセスを開始します。これにより、プロパティが true に設定され、プログレス バーが有効になり、バックグラウンド処理が実行されます。

PCL に移植する前に、UI スレッドから変更を呼び出して進行状況バーが有効になっていることを確認できましたが、PCL では Dispatcher オブジェクトを使用できません。どうすればこれを回避できますか?

ありがとう

ダン

4

3 に答える 3

15

すべての MvvmCross プラットフォームでは、UI アクションが UI スレッド/アパートメントにマーシャリングされる必要がありますが、各プラットフォームではこれが異なります....

これを回避するために、MvvmCross はこれを行うためのクロスプラットフォームの方法を提供します -IMvxViewDispatcherProvider注入されたオブジェクトを使用します。

たとえば、WindowsPhone では、IMvxViewDispatcherProvider最終的MvxMainThreadDispatcherhttps://github.com/slodge/MvvmCross/blob/vnext/Cirrious/Cirrious.MvvmCross.WindowsPhone/Views/MvxMainThreadDispatcher.csで提供されます。

InvokeOnMainThreadこれは次を使用して実装します。

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

        return true;
    }

ViewModel のコードの場合:

  • あなたの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);
    }
}

だから...あなたのViewModelはただ呼び出すことができますInvokeOnMainThread(() => DoStuff());


もう 1 つの注意点は、MvvmCross が、メソッドを介してMvxViewModel(または実際には任意のMvxNotifyPropertyChangedオブジェクトで)通知されるプロパティ更新の UI スレッド変換を自動的に行うことです。以下を参照してください。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));
                });
    }

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


この呼び出しの自動マーシャリングはRaisePropertyChanged()、ほとんどの状況でうまく機能しますが、バックグラウンド スレッドから多くの変更されたプロパティを発生させると、少し非効率になる可能性があります。多くのスレッド コンテキストの切り替えが発生する可能性があります。ほとんどのコードで意識する必要はありませんが、問題がある場合は、次のようにコードを変更すると役立ちます。

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

に:

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

を使用する場合ObservableCollection、MvvmCross はこれらのクラスによって起動されるまたはイベントのスレッド マーシャリングを行わないことに注意してください。したがって、これらの変更をマーシャリングするのは開発者の責任です。INotifyPropertyChangedINotifyCollectionChanged

理由: ObservableCollectionMS および Mono コード ベースに存在するため、MvvmCross がこれらの既存の実装を変更する簡単な方法はありません。

于 2013-01-20T21:37:17.377 に答える
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 インスタンスとしてポータブル クラス ライブラリに渡すだけです。

于 2013-01-20T18:24:32.540 に答える
1

より簡単な別のオプションは、クラスのコンストラクターに SynchronizationContext.Current への参照を格納することです。次に、後で _context.Post(() => ...) を使用して、コンテキスト (WPF/WinRT/SL の UI スレッド) で呼び出すことができます。

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

   private void MyCallbackOnAnotherThread()
   {
      _context.Post(() => UpdateTheUi());
   }
}
于 2013-03-28T00:53:18.660 に答える