WPFのMVVM-ViewModelにモデルの変更を警告する方法...または私は?


112

私はMVVMの記事をいくつか読んでいます。主にこれこれです。

私の具体的な質問は次のとおりです。モデルの変更をモデルからビューモデルに伝えるにどうすればよいですか。

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

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

「なぜあなたはそれをしたいのですか?」という形式の回答/コメントがあるのではないかと思います。コメントなので、これが私のプログラムの説明です。私はMVVMを使い始めたばかりなので、おそらく設計全体に問題があります。簡単に説明します。

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

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

良くも悪くも、私はモデルのようなだけではないクラスが含まれている必要があることを態度を取ったPlayingCardDeckだけでなく、BlackJackGameゲーム全体の状態を維持し、そしてプレイヤーがなくなっバストを持っている場合、ディーラーがカードを引くために持って知っているクラス、およびプレーヤーとディーラーの現在のスコア(21未満、21、バストなど)。

BlackJackGame私は「DrawCard」のようなメソッドを公開し、それがいることを私に発生したカードは、以下のような、プロパティを描画する際CardScore、およびIsBust更新されるべきであると、これらの新しい値はViewModelにに伝達。おそらくそれは間違った考えですか?

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

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


3
あなたが説明している相互作用は、標準のイベントメカニズムのように聞こえます。モデルはというイベントを公開OnBustでき、VMはそれをサブスクライブできます。IEAのアプローチも使用できると思います。
code4life 2013年

私が正直に言うと、本当のブラックジャック「アプリ」をどこに作成すれば、私のデータはサービス/プロキシのいくつかのレイヤーとA + B = Cに似た徹底的なレベルのユニットテストの背後に隠れます。それはプロキシになります変更を通知する/ service。
Meirion Hughes 2013

1
皆に感謝します!残念ながら、私は1つの回答しか選択できません。私はレイチェルズを選んでいます。それは、追加のアーキテクチャアドバイスと元の質問の整理のためです。しかし、多くの素晴らしい答えがあり、私はそれらに感謝します。-Dave
デイブ・


2
FWIW:ドメインごとにVMとMの両方を維持するという複雑さの問題に数年間苦労した後、私は両方ともDRYに失敗すると思います。「ドメインインターフェース」と「ViewModelインターフェース」の2つのインターフェースを1つのオブジェクトに置くことで、必要な関心事の分離をより簡単に行うことができます。このオブジェクトは、混乱や同期の欠落なしに、ビジネスロジックとビューロジックの両方に渡すことができます。そのオブジェクトは「アイデンティティオブジェクト」であり、エンティティを一意に表します。ドメインコードとビューコードの分離を維持するには、クラス内でこれを行うためのより良いツールが必要です。
ToolmakerSteve

回答:


61

モデルが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");
}

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

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

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

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

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

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

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、簡単にするために省略しています)


3
より一般的には、すべてのビジネスロジック/ルールがモデルに入りますか?カードを21枚まで(ただし、ディーラーは17枚のまま)、カードを分割できるなど、すべてのロジックはどこにありますか。すべてモデルクラスに属していると想定し、そのために必要だと感じました。モデル内のBlacJackGameコントローラクラス。私はまだこれを理解しようとしているので、例/参照をいただければ幸いです。例としてのブラックジャックのアイデアは、ビジネスロジック/ルールがMVCパターンのモデルクラスにある最も確実なiOSプログラミングのiTunesクラスから引き上げられました。
デイブ

3
@Daveはい、DrawCard()メソッドは他のゲームロジックと共にViewModelにあります。理想的なMVVMアプリケーションでは、ViewModelを作成し、テストスクリプトやコマンドプロンプトウィンドウなどからメソッドを実行するだけで、UIなしでアプリケーションを実行できます。モデルは通常、生データと基本的なデータ検証を含むデータモデルのみです。
レイチェル

6
レイチェルに助けてくれてありがとう。これについてもう少し調査するか、別の質問を書く必要があります。ゲームロジックの場所についてはまだ混乱しています。あなた(および他の人)はそれをViewModelに置くことを提唱し、他の人は "ビジネスロジック"と言います。私の場合、ゲームルールであり、ゲームの状態はモデルに属しています(例:msdn.microsoft.com/en-usを参照) /library/gg405484%28v=pandp.40%29.aspx)およびstackoverflow.com/questions/10964003/…)。この単純なゲームでは、おそらくそれほど重要ではないことを認識しています。しかし、知っておくとよいでしょう。THXS!
デイブ

1
@Dave「ビジネスロジック」という用語を誤って使用し、アプリケーションロジックと混同している可能性があります。リンクしたMSDNの記事を引用すると、「再利用の機会を最大化するには、モデルにユースケース固有またはユーザータスク固有の動作またはアプリケーションロジックを含めないでください」および「通常、ビューモデルは、表現できるコマンドまたはアクションを定義しますUIで、ユーザーが呼び出すことができます」。したがって、のようなものDrawCardCommand()はViewModelにありますが、必要に応じてコマンドが呼び出すメソッドをBlackjackGameModel含むオブジェクトがあると思いますDrawCard()
Rachel

2
メモリリークを回避します。WeakEventパターンを使用します。joshsmithonwpf.wordpress.com/2009/07/11/…–
JJS

24

短い答え:詳細によって異なります。

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

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

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

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


こんにちは、あなたが作ったリストは素晴らしいです。ただし、7および8で問題があります。特に、INotifyPropertyChangedを実装していないViewModelがあります。子自体のリストを含む子のリストが含まれます(WPF TreeviewコントロールのViewModelとして使用されます)。UserControl DataContext ViewModelを子(TreeviewItems)のプロパティの変更に「リッスン」させるにはどうすればよいですか?INotifyPropertyChangedを実装するすべての子要素を正確にサブスクライブするにはどうすればよいですか?または別の質問をする必要がありますか?
イゴール

4

あなたの選択:

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

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

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

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

例(ILにFODYを使用してPropertyChangedレイザーを織り込みます):

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

     public event PropertyChangedEventHandler PropertyChanged;
}

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

INotifyPropertyChangedルートの優れた点は、Extended ObservableCollectionを使用してチェーン化することです。したがって、近くのpocoオブジェクトをコレクションにダンプし、コレクションを聴きます...何かが変わった場合、どこでも、それについて学びます。

私は正直に言うと、これは「コンパイラーによってINotifyPropertyChangedが自動的に処理されなかった理由」の議論に加わることができます。つまり、デフォルトでINotifyPropertyChangedを実装します。しかし、そうではなく、最小限の労力を必要とする最良のルートは、ILウィービング(具体的にはFODY)を使用することです。


4

かなり古いスレッドですが、何度も検索した後、私は自分のソリューションを思いつきました:A PropertyChangedProxy

このクラスを使用すると、他のユーザーのNotifyPropertyChangedに簡単に登録し、登録されたプロパティに対して呼び出された場合に適切なアクションを実行できます。

独自に変更できるモデルプロパティ「Status」があり、ビューにも通知されるように、その「Status」プロパティで独自のPropertyChangedを起動するようにViewModelに自動的に通知する必要がある場合の例を以下に示します。 )

public class MyModel : INotifyPropertyChanged
{
    private string _status;
    public string Status
    {
        get { return _status; }
        set { _status = value; OnPropertyChanged(); }
    }

    // Default INotifyPropertyChanged
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        var handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

public class MyViewModel : INotifyPropertyChanged
{
    public string Status
    {
        get { return _model.Status; }
    }

    private PropertyChangedProxy<MyModel, string> _statusPropertyChangedProxy;
    private MyModel _model;
    public MyViewModel(MyModel model)
    {
        _model = model;
        _statusPropertyChangedProxy = new PropertyChangedProxy<MyModel, string>(
            _model, myModel => myModel.Status, s => OnPropertyChanged("Status")
        );
    }

    // Default INotifyPropertyChanged
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        var handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

そしてここにクラス自体があります:

/// <summary>
/// Proxy class to easily take actions when a specific property in the "source" changed
/// </summary>
/// Last updated: 20.01.2015
/// <typeparam name="TSource">Type of the source</typeparam>
/// <typeparam name="TPropType">Type of the property</typeparam>
public class PropertyChangedProxy<TSource, TPropType> where TSource : INotifyPropertyChanged
{
    private readonly Func<TSource, TPropType> _getValueFunc;
    private readonly TSource _source;
    private readonly Action<TPropType> _onPropertyChanged;
    private readonly string _modelPropertyname;

    /// <summary>
    /// Constructor for a property changed proxy
    /// </summary>
    /// <param name="source">The source object to listen for property changes</param>
    /// <param name="selectorExpression">Expression to the property of the source</param>
    /// <param name="onPropertyChanged">Action to take when a property changed was fired</param>
    public PropertyChangedProxy(TSource source, Expression<Func<TSource, TPropType>> selectorExpression, Action<TPropType> onPropertyChanged)
    {
        _source = source;
        _onPropertyChanged = onPropertyChanged;
        // Property "getter" to get the value
        _getValueFunc = selectorExpression.Compile();
        // Name of the property
        var body = (MemberExpression)selectorExpression.Body;
        _modelPropertyname = body.Member.Name;
        // Changed event
        _source.PropertyChanged += SourcePropertyChanged;
    }

    private void SourcePropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == _modelPropertyname)
        {
            _onPropertyChanged(_getValueFunc(_source));
        }
    }
}

1
メモリリークを回避します。WeakEventパターンを使用します。joshsmithonwpf.wordpress.com/2009/07/11/…–
JJS

1
@JJS-OTOH、弱いイベントパターンは危険だと考えてください。個人的には、登録解除(-= my_event_handler)を忘れた場合、メモリリークのリスクがあります。これは、発生する可能性のある、または発生しない可能性があるまれな+予測できないゾンビの問題より追跡が容易だからです。
ToolmakerSteve 2018年

@ToolmakerSteve、バランスのとれた引数を追加していただきありがとうございます。開発者は自分たちの状況で、彼らのために最善を尽くすことをお勧めします。インターネットのソースコードを盲目的に採用しないでください。EventAggregator / EventBusのような一般的に使用されるクロスコンポーネントメッセージング(独自の危険も伴う)のような他のパターン
JJS

2

この記事は役に立ちました:http : //social.msdn.microsoft.com/Forums/vstudio/en-US/3eb70678-c216-414f-a4a5-e1e3e557bb95/mvvm-businesslogic-is-part-of-the-?forum = wpf

私の要約:

MVVM編成の背後にある考え方は、ビューとモデルをより簡単に再利用できるようにすることと、分離されたテストを可能にすることです。ビューモデルはビューエンティティを表すモデルであり、モデルはビジネスエンティティを表します。

後でポーカーゲームを作りたい場合はどうしますか?UIの多くは再利用可能でなければなりません。ゲームロジックがビューモデルにバインドされている場合、ビューモデルを再プログラムせずにこれらの要素を再利用することは非常に困難です。ユーザーインターフェイスを変更したい場合はどうしますか?ゲームロジックがビューモデルロジックに結合されている場合、ゲームがまだ機能していることを再確認する必要があります。デスクトップとWebアプリを作成したい場合はどうでしょうか?ビューモデルにゲームロジックが含まれている場合、アプリケーションロジックは必然的にビューモデルのビジネスロジックと結び付けられるため、これら2つのアプリケーションを並べて維持しようとすると複雑になります。

データ変更通知とデータ検証は、すべてのレイヤー(ビュー、ビューモデル、モデル)で行われます。

モデルには、データ表現(エンティティ)とそれらのエンティティに固有のビジネスロジックが含まれています。カードのデッキは、固有の特性を持つ論理的な「もの」です。優れたデッキでは、重複したカードを入れることはできません。トップカードを取得する方法を公開する必要があります。それが残したよりも多くのカードを配らないことを知る必要があります。このようなデッキの動作は、カードのデッキに固有であるため、モデルの一部です。ディーラーモデル、プレーヤーモデル、ハンドモデルなどもあります。これらのモデルは相互に作用することができます。

ビューモデルは、プレゼンテーションとアプリケーションロジックで構成されます。ゲームの表示に関連するすべての作業は、ゲームのロジックとは別です。これには、画像としての手の表示、ディーラーモデルへのカードの要求、ユーザーの表示設定などが含まれます。

記事の要点:

基本的に、私がこれを説明したいのは、ビジネスロジックとエンティティがモデルを構成するということです。これは特定のアプリケーションが使用しているものですが、多くのアプリケーションで共有できます。

ビューはプレゼンテーション層です-ユーザーと実際に直接インターフェースすることに関連するもの。

ViewModelは基本的に、2つをリンクするアプリケーションに固有の「接着剤」です。

ここに、それらがどのようにインターフェースするかを示す素敵な図があります:

http://reedcopsey.com/2010/01/06/better-user-and-developer-experiences-from-windows-forms-to-wpf-with-mvvm-part-7-mvvm/

あなたの場合-いくつかの詳細に取り組みましょう...

検証:通常、これには2つの形式があります。ユーザー入力に関連する検証は、ViewModel(主に)とViewで行われます(つまり、テキストが入力されないようにする "Numeric" TextBoxがビューで処理されます)。そのため、ユーザーからの入力の検証は通常、VMの問題です。そうは言っても、検証の2番目の「レイヤー」があることがよくあります。これは、使用されているデータがビジネスルールに一致することの検証です。多くの場合、これはモデル自体の一部です。データをモデルにプッシュすると、検証エラーが発生する可能性があります。その後、VMはこの情報をビューに再マッピングする必要があります。

「DBへの書き込み、電子メールの送信など、ビューのない舞台裏での」操作:これは、実際には図の「ドメイン固有の操作」の一部であり、純粋にモデルの一部です。これは、アプリケーションを介して公開しようとしているものです。ViewModelは、この情報を公開するためのブリッジとして機能しますが、操作は純粋なモデルです。

ViewModelの操作:ViewModelにはINPCだけでなく、設定(ユーザーステートなど)の保存など、アプリケーション(ビジネスロジックではない)に固有の操作も必要です。これはアプリによって異なります。アプリによって、同じ「モデル」をインターフェースする場合でも。

それについて考える良い方法-あなたが注文システムの2つのバージョンを作りたいとしましょう。1つ目はWPFで、2つ目はWebインターフェイスです。

注文自体(メールの送信、DBへの入力など)を処理する共有ロジックがモデルです。アプリケーションはこれらの操作とデータをユーザーに公開していますが、2つの方法でそれを行っています。

WPFアプリケーションでは、ユーザーインターフェース(ビューアが操作するもの)は「ビュー」です。Webアプリケーションでは、これは基本的に(少なくとも最終的には)クライアントでJavaScript + HTML + CSSに変換されるコードです。

ViewModelは、使用している特定のビューテクノロジー/レイヤーで機能させるために、モデル(順序に関連するこれらの操作)を適応させるために必要な残りの「接着剤」です。


多分簡単な例は音楽プレーヤーです。モデルには、ライブラリ、アクティブなサウンドファイル、コーデック、プレーヤーロジック、デジタル信号処理コードが含まれます。ビューモデルには、コントロール、視覚化、およびライブラリブラウザーが含まれます。そのすべての情報を表示するために必要なUIロジックはたくさんあります。あるプログラマーが音楽の再生に集中できるようにしながら、別のプログラマーがUIを直感的で楽しいものにすることに集中できるようにするとよいでしょう。ビューモデルとモデルは、これら2人のプログラマが一連のインターフェースについて合意し、別々に作業できるようにする必要があります。
VoteCoffee 2014年

別の良い例はウェブページです。サーバー側のロジックは通常、モデルと同等です。クライアント側のロジックは通常、ビューモデルと同等です。ゲームロジックがサーバーに属し、クライアントに委託されないことは容易に想像できます。
投票コーヒー

2

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

理解するには、PropertyObserverの使用方法についての記事をご覧ください

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


Josh Smithsの優れた記事を参照し、弱いイベントを取り上げていただき、ありがとうございます。
JJS 2015

1

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


1

2008年のMVVM記事の「変更フロー」セクションでわかるように、私は長い間、方向性のあるモデル->モデルの表示->変更のフローの表示を推奨してきました。これにはINotifyPropertyChanged、モデルへの実装が必要です。私の知る限りでは、それが一般的な習慣になってからです。

Josh Smithについて言及したので、彼のPropertyChangedクラスを見てください。これは、モデルのINotifyPropertyChanged.PropertyChangedイベントをサブスクライブするためのヘルパークラスです。

最近、PropertiesUpdaterクラスを作成して、このアプローチをさらに進めることができます。ビューモデルのプロパティは、モデルの1つ以上のプロパティを含む複雑な式として計算されます。


1

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

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


0

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

たとえば、最近ツリービューを生成する必要があるプロジェクトに取り組みました(当然、モデルには階層的な性質がありました)。モデルでは、私は、ChildElements

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

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


階層データを扱う場合は、私のMVVM記事のデモ2ご覧ください
HappyNomad 2013年

0

これは本当に重要な質問のように思えます-それを行うように圧力がない場合でも。TreeViewを含むテストプロジェクトに取り組んでいます。削除などのコマンドにマップされているメニュー項目などがあります。現在、ビューモデル内からモデルとビューモデルの両方を更新しています。

例えば、

public void DeleteItemExecute ()
{
    DesignObjectViewModel node = this.SelectedNode;    // Action is on selected item
    DocStructureManagement.DeleteNode(node.DesignObject); // Remove from application
    node.Remove();                                // Remove from view model
    Controller.UpdateDocument();                  // Signal document has changed
}

これは単純ですが、非常に基本的な欠陥があるようです。典型的な単体テストでは、コマンドを実行してから、ビューモデルで結果を確認します。ただし、2つが同時に更新されるため、モデルの更新が正しいかどうかはテストされません。

したがって、モデルの更新によってビューモデルの更新がトリガーされるようにするには、PropertyObserverのような手法を使用する方がよいでしょう。同じユニットテストは、両方のアクションが成功した場合にのみ機能するようになりました。

これは潜在的な答えではない、と私は理解していますが、それを提示する価値があるようです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.