web-dev-qa-db-ja.com

WPF:コードビハインドのコードなしでViewModelからViewにイベントを通知する方法

私は非常に単純な(私は願っています:))問題:

MVVMでは、Viewは通常、ViewModelのプロパティの変更をリッスンします。ただし、イベントをリッスンして、たとえばVMシグナルのときにViewがアニメーションを開始したり、ウィンドウを閉じたりできるようにしたい場合があります。

NotifyPropertyChangedでboolプロパティを介して(そしてfalseからtrueに変化したときにのみアニメーションを開始して)実行することは可能ですが、ハッキングのように感じられます。

また、コードビハインドのコードなしでそれを実行したいのですが、viewModel.myEvent += handlerを実行すると、ビューをGCできるようにイベントを手動で登録解除することになります-WPFビューはすでにプロパティを「弱く」リッスンするので、Viewで宣言的にのみプログラミングすることをお勧めします。

1つのビューに対して複数のViewModelを切り替える必要があるため、標準の強力なイベントサブスクリプションも不適切です(毎回ビューを作成するとCPU時間がかかりすぎるため)。

アイデアをありがとう(標準的な解決策があれば、msdnへのリンクで十分です)!

40
Tomáš Kafka

いくつかのコメント:

  • weak event pattern を使用して、ビューがまだビューモデルのイベントにアタッチされている場合でも、ビューをGCできるようにすることができます。
  • 1つのビューですでに複数のVMを切り替えている場合、それはハンドラーをアタッチ/デタッチするのに理想的な場所ではないでしょうか?
  • 正確なシナリオに応じて、VMがビューをアニメーション、遷移、およびその他の視覚的変化のトリガーとして使用する状態プロパティを公開するだけにすることができます。 Visual state manager はこの種のものに最適です。
3
Kent Boogaart

このスレッドはかなり古いですが、最近同様に取り組んだものなので$ 0.02を提供します...

他の人が言っているのと似ていますが、ここにいくつかのコードスニペットの例があります...この例は、pub/subを使用して、VM-この場合、GridViewを実行します。gvがVMと同期していることを確認するために再バインドします...

ビュー(サブ):

 using Microsoft.Practices.Composite.Events;
 using Microsoft.Practices.Composite.Presentation.Events;

 private SubscriptionToken getRequiresRebindToken = null;

    private void SubscribeToRequiresRebindEvents()
    {
        this.getRequiresRebindToken =
            EventBus.Current.GetEvent<RequiresRebindEvent>()
            .Subscribe(this.OnRequiresRebindEventReceived, 
                ThreadOption.PublisherThread, false,
                MemoryLeakHelper.DummyPredicate);
    }

    public void OnRequiresRebindEventReceived(RequiresRebindEventPayload payload)
    {
        if (payload != null)
        {
            if (payload.RequiresRebind)
            {
                using (this.gridView.DeferRefresh())
                {
                    this.gridView.Rebind();
                }
            }
        }
    }

    private void UnsubscribeFromRequiresRebindEvents()
    {
        if (this.getRequiresRebindToken != null)
        {
            EventBus.Current.GetEvent<RequiresRebindEvent>()
                .Unsubscribe(this.getRequiresRebindToken);
            this.getRequiresRebindToken = null;
        }
    }

メモリリークを防ぐために、closeメソッドからunsubを呼び出します。

ViewModel(Pub):

 private void PublishRequiresRebindEvent()
 {
      var payload = new RequiresRebindEventPayload();
      payload.SetRequiresRebind();
      EventBus.Current.GetEvent<RequiresRebindEvent>().Publish(payload);
 }

ペイロードクラス

using System;
using Microsoft.Practices.Composite.Presentation.Events;

public class RequiresRebindEvent 
    : CompositePresentationEvent<RequiresRebindEventPayload>
{

}

public class RequiresRebindEventPayload
{
    public RequiresRebindEventPayload()
    {
        this.RequiresRebind = false;
    }

    public bool RequiresRebind { get; private set; }

    public void SetRequiresRebind()
    {
        this.RequiresRebind = true;
    }
}

GUIDを渡すようにコンストラクターを設定することもできます。または、Guidで識別されるものをPubで設定し、subでチェックして、pub/subが同期していることを確認できます。

2
Chris Marcus

imho yYand分離

  1. 状態-データをビュー<-> vm間で前後に移動できるようにする
  2. アクション-ビューモデルの関数/コマンドを呼び出すことができる
  3. 通知-何かが発生したことをビューに通知できるようにし、要素を光らせる、スタイルを切り替える、レイアウトを変更する、別の要素にフォーカスするなどの見た目のアクションを実行させたい.

プロパティバインディングを使用してこれを実行できることは事実ですが、tomasが述べたようにハックのようなものです。いつもこんな感じでした。

ビューモデル(通知)から 'イベント'をリッスンできるようにするための私のソリューションは、データコンテキストの変更をリッスンし、それが変更されたときに、タイプが確認しているvmであることを確認し、イベントを接続します。粗雑ですがシンプルです。

私が本当に望んでいるのは、いくつかの「ビューモデルイベント」トリガーを定義し、xamlのすべてのビュー側で反応し、そうでないものについてはコードビハインドにドロップするだけのハンドラーを提供する簡単な方法です。 xamlで実行可能

1
voidx

より一般的な質問は、「なぜViewModelでこのイベントを処理しようとしているのですか?」です。

答えがアニメーションなどの表示のみのものと関係がある場合、ViewModelはそれについて知る必要がないと主張します:コードビハインド(適切な場合)、Data/Event/PropertyTriggers、および新しいVisualStateManager構成はより良いサービスを提供しますと、ViewとViewModelの間の明確な分離を維持します。

イベントの結果として何かが「発生」する必要がある場合、実際に使用したいのはコマンドパターンです。CommandMangerを使用するか、コードビハインドでイベントを処理し、ビューモデルでコマンドを呼び出すか、またはSystem.Interactivityライブラリに添付された動作。

どちらの方法でも、できるだけViewModelを "純粋"に保つ必要があります。そこにView固有の何かが表示される場合は、おそらく間違っています。 :)

0
JerKimball

アドリアンが言ったように、boolプロパティからアニメーションをトリガーするとき、実際にはイベントに応答しています。具体的には、WPFサブシステムが行うイベントPropertyChangedです。これは、メモリをリークしないように正しくアタッチ/デタッチするように設計されています(イベントを自分で配線するときにこれを忘れて、GCする必要があるオブジェクトへの参照をアクティブにすることでメモリリークを引き起こす可能性があります)。 。

これにより、コントロールのDataContextとしてViewModelを公開し、データバインディングを通じてデータコンテキストのプロパティの変更に正しく応答することができます。

MVVMは、WPFがこれらすべてを提供するため、WPFで特にうまく機能するパターンであり、プロパティの変更をトリガーすることは、実際にはWPFサブシステム全体を使用して目標を達成するための洗練された方法です。

0
Jim Wallace