76

ビューに属するディスパッチャーにアクセスできるはずです。ビューモデルに渡す必要があります。しかし、ビューはViewModelについて何も知らないはずなので、どのように渡しますか?インターフェイスを導入するか、インスタンスに渡す代わりに、ビューによって書き込まれるグローバルディスパッチャーシングルトンを作成しますか?MVVMアプリケーションとフレームワークでこれをどのように解決しますか?

Dispatcher.Current編集:私のViewModelsはバックグラウンドスレッドで作成される可能性があるため、ViewModelのコンストラクターで作成することはできないことに注意してください。

4

16 に答える 16

47

インターフェイスIContextを使用して Dispatcher を抽象化しました。

public interface IContext
{
   bool IsSynchronized { get; }
   void Invoke(Action action);
   void BeginInvoke(Action action);
}

これには、ViewModel をより簡単に単体テストできるという利点があります。
MEF (Managed Extensibility Framework) を使用して、ViewModel にインターフェイスを挿入します。別の可能性は、コンストラクターの引数です。でも、私はMEFを使った注射の方が好きです。

更新(コメントのペーストビンリンクの例):

public sealed class WpfContext : IContext
{
    private readonly Dispatcher _dispatcher;

    public bool IsSynchronized
    {
        get
        {
            return this._dispatcher.Thread == Thread.CurrentThread;
        }
    }

    public WpfContext() : this(Dispatcher.CurrentDispatcher)
    {
    }

    public WpfContext(Dispatcher dispatcher)
    {
        Debug.Assert(dispatcher != null);

        this._dispatcher = dispatcher;
    }

    public void Invoke(Action action)
    {
        Debug.Assert(action != null);

        this._dispatcher.Invoke(action);
    }

    public void BeginInvoke(Action action)
    {
        Debug.Assert(action != null);

        this._dispatcher.BeginInvoke(action);
    }
}
于 2010-03-01T08:47:06.520 に答える
46

なぜあなたは使わないのですか

 System.Windows.Application.Current.Dispatcher.Invoke(
         (Action)(() => {ObservableCollectionMemeberOfVM.Add("xx"); } ));

GUIディスパッチャーへの参照を保持する代わりに。

于 2012-05-28T20:26:40.957 に答える
19

実際にはディスパッチャは必要ない場合があります。ビューモデルのプロパティをビューのGUI要素にバインドすると、WPFバインドメカニズムは、ディスパッチャーを使用してGUI更新をGUIスレッドに自動的にマーシャリングします。


編集:

この編集は、IsakSavoのコメントに対応しています。

プロパティへのバインドを処理するためのMicrosoftのコード内には、次のコードがあります。

if (Dispatcher.Thread == Thread.CurrentThread)
{ 
    PW.OnPropertyChangedAtLevel(level);
} 
else 
{
    // otherwise invoke an operation to do the work on the right context 
    SetTransferIsPending(true);
    Dispatcher.BeginInvoke(
        DispatcherPriority.DataBind,
        new DispatcherOperationCallback(ScheduleTransferOperation), 
        new object[]{o, propName});
} 

このコードは、UIの更新をスレッドUIスレッドにマーシャリングするため、別のスレッドからのバインディングの一部としてプロパティを更新した場合でも、WPFはUIスレッドへの呼び出しを自動的にシリアル化します。

于 2010-03-01T08:53:01.983 に答える
15

ViewModel を取得して、現在のディスパッチャーをメンバーとして格納します。

ViewModel がビューによって作成された場合、作成時の現在のディスパッチャーがビューのディスパッチャーになることがわかります。

class MyViewModel
{
    readonly Dispatcher _dispatcher;
    public MyViewModel()
    {
        _dispatcher = Dispatcher.CurrentDispatcher;
    }
}
于 2010-03-01T07:54:09.770 に答える
5

もう1つの一般的なパターン(現在フレームワークで多く使用されている)はSynchronizationContextです。

これにより、同期および非同期でディスパッチできます。現在のスレッドに現在のSynchronizationContextを設定することもできます。これは、簡単にモックされることを意味します。DispatcherSynchronizationContextは、WPFアプリによって使用されます。彼らのSynchronizationContextの他の実装は、WCFとWF4によって使用されます。

于 2011-06-24T20:15:01.847 に答える
1

別のスレッドでバインドされたコレクションを変更するためのディスパッチャのみが必要な場合は、こちらの SynchronizationContextCollection を参照してくださいhttp://kentb.blogspot.com/2008/01/cross-thread-collection-binding-in-wpf.html

うまく機能します。私が見つけた唯一の問題は、ASP.NET 同期コンテキストで SynchronizationContextCollection プロパティを持つ View Models を使用する場合ですが、簡単に回避できます。

HTH サム

于 2010-10-04T09:01:22.927 に答える
1

こんにちは、最初の投稿から 8 か月が経過しているため、手遅れかもしれません... Silverlight mvvm アプリケーションで同じ問題が発生しました。そして、私はこのような私の解決策を見つけました。私が持っているモデルとビューモデルごとに、コントローラーと呼ばれるクラスもあります。そのように

public class MainView : UserControl  // (because it is a silverlight user controll)
public class MainViewModel
public class MainController

私の MainController は、モデルとビューモデル間のコマンドと接続を担当しています。コンストラクターで、ビューとそのビューモデルをインスタンス化し、ビューのデータコンテキストをそのビューモデルに設定します。

mMainView = new MainView();
mMainViewModel = new MainViewModel();
mMainView.DataContext = mMainViewModel; 

//(私の命名規則では、メンバー変数に接頭辞 m を付けています)

MainView の型にパブリック プロパティもあります。そのように

public MainView View { get { return mMainView; } }

(この mMainView はパブリック プロパティのローカル変数です)

そして今、私は終わりました。このようにUI theradにディスパッチャを使用する必要があります...

mMainView.Dispatcher.BeginInvoke(
    () => MessageBox.Show(mSpWeb.CurrentUser.LoginName));

(この例では、コントローラーに sharepoint 2010 のログイン名を取得するように要求していましたが、必要なことは実行できます)

次のように、app.xaml でルート ビジュアルを定義する必要もあります。

var mainController = new MainController();
RootVisual = mainController.View;

これは私のアプリケーションによって私を助けました。たぶんそれもあなたを助けることができます...

于 2010-11-27T15:59:19.777 に答える
1

UI Dispatcher を ViewModel に渡す必要はありません。UI Dispatcher は、現在のアプリケーション シングルトンから利用できます。

App.Current.MainWindow.Dispatcher

これにより、ViewModel が View に依存するようになります。アプリケーションによっては、それでよい場合とそうでない場合があります。

于 2011-10-26T17:53:33.917 に答える
1

WPF および Windows ストア アプリの場合:-

       System.Windows.Application.Current.Dispatcher.Invoke((Action)(() => {ObservableCollectionMemeberOfVM.Add("xx"); } ));

GUIディスパッチャーへの参照を維持することは、実際には正しい方法ではありません。

それが機能しない場合 (Windows Phone 8 アプリの場合など) は、次を使用します。

       Deployment.Current.Dispatcher
于 2014-04-30T06:47:38.077 に答える
0

私は別の(最も簡単な)方法を見つけました:

Dispatcher で呼び出す必要があるビュー モデル アクションに追加します。

public class MyViewModel
{
    public Action<Action> CallWithDispatcher;

    public void SomeMultithreadMethod()
    {
        if(CallWithDispatcher != null)
            CallWithDispatcher(() => DoSomethingMetod(SomeParameters));
    }
}

そして、このアクション ハンドラーをビュー コンストラクターに追加します。

    public View()
    {
        var model = new MyViewModel();

        DataContext = model;
        InitializeComponent();

        // Here 
        model.CallWithDispatcher += act => _taskbarIcon.Dispatcher
            .BeginInvoke(DispatcherPriority.Normal, act) ;
    }

これでテストに問題はなくなり、実装も簡単になりました。サイトに追加しました

于 2011-09-14T11:29:06.283 に答える
0

uNhAddInsを使用すると、非同期の動作を簡単に作成できます。ここを見てください

そして、Castle Windsor(uNhAddInsなし)で動作させるには、いくつかの変更が必要だと思います

于 2010-10-23T04:56:44.853 に答える
0

を使用してアプリケーションディスパッチャーにアクセスできる場合、ディスパッチャーを渡す必要はありません

Dispatcher dis = Application.Current.Dispatcher 
于 2020-05-31T02:39:13.033 に答える
-1

私のWPFプロジェクトのいくつかは、同じ状況に直面しています。MainViewModel(シングルトンインスタンス)で、CreateInstance()静的メソッドがディスパッチャーを取得しました。そして、createインスタンスがビューから呼び出されるので、そこからDispatcherを渡すことができます。そして、ViewModelテストモジュールはCreateInstance()をパラメーターなしで呼び出します。

ただし、複雑なマルチスレッドシナリオでは、現在のウィンドウの適切なディスパッチャーを取得するために、ビュー側にインターフェイスを実装することをお勧めします。

于 2010-03-01T09:03:07.443 に答える
-1

この議論には少し遅れているかもしれませんが、1 つの素晴らしい記事を見つけましたhttps://msdn.microsoft.com/en-us/magazine/dn605875.aspx

1段落あります

さらに、ビュー レイヤーの外側にあるすべてのコード (つまり、ViewModel レイヤーとモデル レイヤー、サービスなど) は、特定の UI プラットフォームに関連付けられた型に依存しないようにする必要があります。Dispatcher (WPF/Xamarin/Windows Phone/Silverlight)、CoreDispatcher (Windows ストア)、または ISynchronizeInvoke (Windows フォーム) を直接使用することはお勧めできません。(SynchronizationContext はわずかに優れていますが、かろうじて優れています。) たとえば、インターネット上には、非同期処理を行ってから Dispatcher を使用して UI を更新するコードがたくさんあります。移植性が高く、煩わしくない解決策は、非同期作業に await を使用し、Dispatcher を使用せずに UI を更新することです。

async/await を適切に使用できる場合、これは問題ではないと仮定します。

于 2017-10-05T00:57:09.827 に答える