124

私はいくつかのMVVMの記事、主にこれこれを調べています。

私の具体的な質問は次のとおりです。モデルからViewModelへのモデルの変更をどのように伝達しますか?

Joshの記事では、彼がこれを行っているとは思いません。ViewModelは常にモデルにプロパティを要求します。レイチェルの例では、彼女はモデルを実装INotifyPropertyChangedし、モデルからイベントを発生させますが、それらはビュー自体が消費するためのものです(彼女がこれを行う理由の詳細については、彼女の記事/コードを参照してください)。

モデルがモデルプロパティの変更についてViewModelに警告する例はどこにもありません。これは、おそらくそれが何らかの理由で行われていないのではないかと心配しています。 モデルの変更をViewModelに警告するパターンはありますか? (1)モデルごとに複数のViewModelがあると考えられ、(2)ViewModelが1つしかない場合でも、モデルに対するアクションによって他のプロパティが変更される可能性があるため、必要と思われます。

「どうしてそんなことをしたいの?」という形の回答・コメントがあるのではないかと思います。コメントなので、これが私のプログラムの説明です。私はMVVMを初めて使用するので、おそらく私の設計全体に欠陥があります。簡単に説明します。

私は、「顧客」または「製品」クラスよりも(少なくとも私にとっては!)興味深いものをプログラミングしています。私はブラックジャックをプログラミングしています。

コードが背後になく、ViewModelのプロパティとコマンドへのバインドに依存しているビューがあります(Josh Smithの記事を参照)。

良くも悪くも、モデルには、などのクラスだけでなく、ゲーム全体の状態を維持し、プレーヤーがいつバストしたかを知っているクラスも含まれるべきであるという態度を取りましPlayingCardDeckBlackJackGameディーラーはカードを引く必要があります。プレーヤーとディーラーの現在のスコアは何ですか(21、21未満、バストなど)。

「 DrawCard BlackJackGame」のようなメソッドを公開したところ、カードが描画されたときに、、などのプロパティを更新する必要がCardScoreありIsBust、これらの新しい値がViewModelに伝達されることに気付きました。おそらくそれは間違った考えですか?

ViewModelがDrawCard()メソッドを呼び出したという態度を取ることができるので、更新されたスコアを要求し、彼がバストであるかどうかを確認する必要があります。意見?

私のViewModelには、トランプの実際の画像(スーツ、ランクに基づく)を取得し、それをビューで使用できるようにするロジックがあります。モデルはこれに関係するべきではありません(おそらく他のViewModelはトランプの画像の代わりに数字を使用するでしょう)。もちろん、モデルにブラックジャックゲームの概念すら持たないようにし、それをViewModelで処理する必要があると言う人もいるかもしれません。

4

11 に答える 11

68

モデルが変更を ViewModel に通知するようにするには、INotifyPropertyChangedを実装し、ViewModel が PropertyChange 通知を受け取るようにサブスクライブする必要があります。

コードは次のようになります。

// Attach EventHandler
PlayerModel.PropertyChanged += PlayerModel_PropertyChanged;

...

// When property gets changed in the Model, raise the PropertyChanged 
// event of the ViewModel copy of the property
PlayerModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
    if (e.PropertyName == "SomeProperty")
        RaisePropertyChanged("ViewModelCopyOfSomeProperty");
}

ただし、通常、これは、複数のオブジェクトがモデルのデータに変更を加える場合にのみ必要ですが、通常はそうではありません。

PropertyChanged イベントをアタッチする Model プロパティへの参照を実際に持っていない場合は、PrismEventAggregatorや MVVM Lightなどのメッセージング システムを使用できますMessenger

ブログでメッセージング システムの概要を簡単に説明していますが、要約すると、任意のオブジェクトがメッセージをブロードキャストでき、任意のオブジェクトがサブスクライブして特定のメッセージをリッスンできます。したがって、PlayerScoreHasChangedMessageあるオブジェクトから をブロードキャストすると、別のオブジェクトがサブスクライブしてこれらのタイプのメッセージをリッスンし、メッセージを受信しPlayerScoreたときにそのプロパティを更新できます。

しかし、これはあなたが説明したシステムには必要ないと思います。

理想的な MVVM の世界では、アプリケーションは ViewModel で構成され、モデルはアプリケーションを構築するために使用される単なるブロックです。DrawCard()通常、データのみが含まれているため、 (ViewModel にある)などのメソッドはありません。

したがって、おそらく次のようなプレーンな Model データ オブジェクトがあるでしょう。

class CardModel
{
    int Score;
    SuitEnum Suit;
    CardEnum CardValue;
}

class PlayerModel 
{
    ObservableCollection<Card> FaceUpCards;
    ObservableCollection<Card> FaceDownCards;
    int CurrentScore;

    bool IsBust
    {
        get
        {
            return Score > 21;
        }
    }
}

そして、次のようなViewModelオブジェクトがあります

public class GameViewModel
{
    ObservableCollection<CardModel> Deck;
    PlayerModel Dealer;
    PlayerModel Player;

    ICommand DrawCardCommand;

    void DrawCard(Player currentPlayer)
    {
        var nextCard = Deck.First();
        currentPlayer.FaceUpCards.Add(nextCard);

        if (currentPlayer.IsBust)
            // Process next player turn

        Deck.Remove(nextCard);
    }
}

(上記のオブジェクトはすべて を実装する必要がありますINotifyPropertyChangedが、簡単にするために省略しました)

于 2013-03-15T19:48:10.317 に答える
25

簡単な答え: 詳細によって異なります。

あなたの例では、モデルは「独自に」更新されており、これらの変更はもちろん何らかの形でビューに伝播する必要があります。ビューはビューモデルに直接アクセスすることしかできないため、モデルがこれらの変更を対応するビューモデルに伝達する必要があることを意味します。これを行うための確立されたメカニズムはもちろんINotifyPropertyChangedです。つまり、次のようなワークフローが得られます。

  1. ビューモデルが作成され、モデルがラップされます
  2. PropertyChangedビューモデルはモデルのイベントをサブスクライブします
  3. Viewmodel はビューの として設定されDataContext、プロパティはバインドされます。
  4. ビューモデルのビュー トリガー アクション
  5. ビューモデルはモデルのメソッドを呼び出します
  6. モデル自体の更新
  7. Viewmodel はモデルを処理し、それに応じPropertyChangedて独自のモデルを発生させますPropertyChanged
  8. ビューはバインディングの変更を反映し、フィードバック ループを閉じます

一方、モデルにビジネス ロジックがほとんど含まれていない (またはまったく含まれていない) 場合、またはその他の理由 (トランザクション機能を取得するなど) で、各ビューモデルにラップされたモデルを「所有」させることにした場合、モデルへのすべての変更はパススルーされます。ビューモデルなので、そのような配置は必要ありません。

このような設計については、別の MVVM の質問hereで説明しています。

于 2013-03-15T19:17:41.837 に答える
4

あなたの選択:

  • INotifyPropertyChangedを実装します
  • イベント
  • プロキシマニピュレータを備えたPOCO

私が見ているように、これINotifyPropertyChangedは.Netの基本的な部分です。つまり、そのSystem.dll。「モデル」に実装することは、イベント構造を実装することに似ています。

純粋なPOCOが必要な場合は、プロキシ/サービスを介してオブジェクトを効果的に操作する必要があります。その後、プロキシをリッスンすることでViewModelに変更が通知されます。

個人的には、INotifyPropertyChangedを大まかに実装してから、 FODYを使用してダーティな作業を行っています。見た目も感じもPOCO。

例(FODYを使用してPropertyChangedレイザーをILウィーブする):

public class NearlyPOCO: INotifyPropertyChanged
{
     public string ValueA {get;set;}
     public string ValueB {get;set;}

     public event PropertyChangedEventHandler PropertyChanged;
}

次に、ViewModelにPropertyChangedをリッスンさせて変更を確認できます。またはプロパティ固有の変更。

INotifyPropertyChangedルートの利点は、ExtendedObservableCollectionでチェーン化することです。したがって、近くのpocoオブジェクトをコレクションにダンプし、コレクションをリッスンします...何かが変更された場合は、どこでも、それについて学習します。

正直なところ、これは「コンパイラによってINotifyPropertyChangedが自動的に処理されなかった理由」の議論に加わる可能性があります。これは次のように展開されます。c#のすべてのオブジェクトには、オブジェクトの一部が変更されたかどうかを通知する機能が必要です。つまり、デフォルトでINotifyPropertyChangedを実装します。しかし、そうではなく、最小限の労力で済む最善のルートは、ILウィービング(具体的にはFODY)を使用することです。

于 2013-03-15T19:30:34.493 に答える
2

INotifyPropertyChangedおよびINotifyCollectionChangedに基づく通知は、まさに必要なものです。プロパティの変更へのサブスクリプション、プロパティ名のコンパイル時の検証、メモリリークの回避で生活を簡素化するために、JoshSmithのMVVMFoundationのPropertyObserverを使用することをお勧めします。このプロジェクトはオープンソースであるため、ソースからそのクラスだけをプロジェクトに追加できます。

理解するには、PropertyObserverの使用方法についてこの記事を読んでください。

また、Reactive Extensions(Rx)を詳しく見てください。モデルからIObserver<T>を公開し、ビューモデルでサブスクライブできます。

于 2013-03-16T12:05:43.043 に答える
1

みんなはこれに答える素晴らしい仕事をしましたが、このような状況では、MVVMパターンが苦痛だと本当に感じているので、監視コントローラーまたはパッシブビューアプローチを使用して、少なくともモデルオブジェクトのバインディングシステムを手放します独自に変更を生成します。

于 2013-03-15T22:31:14.300 に答える
1

Model 内にINotifyPropertyChangedを実装し、ViewModel 内でリッスンすることは何の問題もありません。実際、XAML でモデルのプロパティにドットを挿入することもできます: {Binding Model.ModelProperty}

依存/計算された読み取り専用プロパティに関しては、 https ://github.com/StephenCleary/CalculatedProperties よりも優れた単純なものを見たことがありません。これは非常にシンプルですが、信じられないほど便利です。実際には「MVVM 用の Excel 数式」です。Excel と同じように機能し、Excel が数式セルに変更を反映するのと同じように機能します。

于 2017-01-04T11:23:22.227 に答える
0

ビューモデルがサブスクライブする必要があるモデルからイベントを発生させることができます。

たとえば、私は最近、ツリービューを生成する必要があるプロジェクトに取り組みました (当然のことながら、モデルには階層的な性質がありました)。モデルには、 と呼ばれる観察可能なコレクションがありましたChildElements

ビューモデルでは、オブジェクトへの参照をモデルに保存しCollectionChanged、observablecollection のイベントを次のようにサブスクライブしModelObject.ChildElements.CollectionChanged += new CollectionChangedEventHandler(insert function reference here)ました。

その後、モデルで変更が発生すると、ビューモデルに自動的に通知されます。を使用して同じ概念に従うことができますがPropertyChanged、それを機能させるには、モデルからプロパティ変更イベントを明示的に発生させる必要があります。

于 2013-03-15T18:47:12.780 に答える