2

MVVM の設計に大きな問題があります。ViewModel内で、ネストされたオブジェクトのさらにpropertchangedを含む、内部のネストされたオブジェクトのすべてのPropertyChangedをキャッチしようとしていますが、その方法がわかりません。

これが私の構造です:

class MyVM
{

  public MyVM()
  {
    this.SomeData = new SomeData();
    this.SomeData.NestedObj = new MyNestedDat();
    this.SomeData.Str = "This tiggers propertychanged inside MyDat class";
    // this triggers propertychanged event inside MyNestedDat class
    this.SomeData.NestedObj.Num = 123;
  }

  // and here should be a method where i catch all possibe propertychanges from my nested objets and their nested objets, how do i do that?

  public MyDat SomeData
  {
    get;
    set;
  }

}

class MyDat : INotifyPropertyChanged
{
  private string str;
  public string Str;
  {
    get { return this.str;}
    set
    {
      this.str = value;
      this.PropertyChanged(this, "Str");
    }
  }


  publicMyNestedDat NestedObj
  {
   get;
   set;
  }
}

class MyNestedDat : INotifyPropertyChanged
{
  private int num;
  public int Num
  {
    get{ return this.num;}
    set
    {
      this.num = value;
      this.PropertyChanged(this, "Num");
    }
  }
}

これを機能させるにはどうすればよいですか?私はどこから始めればいいのか本当にわかりません。

MyNestedDat クラスは PropertyChanged をスローし、MyDat クラスは propertychanged をスローし、ビューモデル内でそれらすべてをキャッチしたいと考えています。どうやってやるの?

4

3 に答える 3

1

私の意見では、あなたが求めていることにはいくつかの概念的な問題があります。自分のシナリオに適した (満足できる) ソリューションが得られたと想像して、次のことを検討してください。

  • 別のレイヤーを追加するとどうなりますか? それでも同じように機能すると思いますか?
  • プロパティの変更を伝播 (viewModel1.propA通知viewModel2.PropA) する必要がありますか?
  • プロパティの変更を変換 (viewModel1.SomeProp通知ViewModel2.AnotherProp) する必要がありますか?
  • パフォーマンスは懸念事項ですか?多くのレベルでプロパティ変更イベントを伝播する必要がある場合、これはどのように機能しますか?

これは、現在のアプローチが正しい道ではないという警鐘を鳴らしているはずです。

必要なのは、viewModel が互いの存在を知る必要さえないように、疎結合の方法で viewModel 間の通信を提供する方法です。これの優れた点は、プロパティの変更だけでなく、他の状況でも機能することです。

プロパティ変更イベントの場合、1 つの viewModel が何かがいつ発生したかを知りたいと考えています (プロパティ変更イベント以外のものである可能性があります)。これは、他のビューモデルが「ねえ、プロパティが変更されました」(または「私の状態が変更されました」、「そのデータベース呼び出しが終了しました」など) と言う何らかの方法が必要であることを意味します。

C# では、この機能を提供するイベントを提供できます...ただし、オブジェクトがお互いを認識できるようになったため、以前と同じ問題が発生します。

この問題を克服するには、別のオブジェクトであるメディエーター (Messengerこの例ではそれを呼び出します) が必要です。このオブジェクトの唯一の目的は、オブジェクト間でやり取りされるメッセージを処理して、オブジェクトが互いに無知で生きられるようにすることです。

大まかな考え方はこうです。通知を提供するviewModelでは、次のようなことをするかもしれません:

public string MyProp
{
    get { return _myProp; }
    set
    {
        _mProp = value;
        OnPropertyChanged("MyProp");
        Messenger.PostMessage(new VMChangedMessage { ViewModel = this, PropertyName = "MyProp" });
    }
}

そして、イベントに関心のあるviewModelでは、次のようなことをするかもしれません:

public class ViewModel2
{
    public ViewModel2()
    {
        Messenger.Subscribe<VMChangedMessage>(handleMessage);
    }

    private void handleMessage(VMChangedMessage msg)
    {
        // Do something with the information here...
    }
}

2 つのビューモデルが互いに参照することはないことに注意してください。それらは疎結合になりました。

すでに利用可能な既存の実装が多数あり、独自の実装を作成することは難しくありません (メッセンジャーは基本的に、特定のメッセージに関心のあるオブジェクトのリストを保持し、関係者に通知する必要があるときにリストを繰り返します)。 . 異なる方法で実装できるものがいくつかあります (オブジェクトに情報をカプセル化するのではなく、文字列メッセージを渡すだけの実装もあれば、オブザーバーのクリーンアップを自動的に処理する実装もあります)。

メッセンジャークラスを含むJosh Smiths (excellent) MVVM Foundationを使用することをお勧めします。また、オープンソースであるため、どのように機能するかを確認できます。

于 2013-04-09T16:51:16.497 に答える
1

PropertyChangedEventArgsPropertyNameに何を含める必要があるかについて明確な制約はありません。

ネストされた (子) オブジェクトの INotifyPropertyChanged にサブスクライブする を参照してください。

例を次に示します。

class A : BaseObjectImplementingINotifyPropertyChanged {

  private string m_name;
  public string Name {
    get { return m_name; }
    set {
      if(m_name != value) {
        m_name = value;
        RaisePropertyChanged("Name");
      }
    }
  }
}

class B : BaseObjectImplementingINotifyPropertyChanged {

  private A m_a;
  public A A {
    get { return m_a; }
    set {
      if(m_a != value) {
        if(m_a != null) m_a.PropertyChanged -= OnAPropertyChanged;
        m_a = value;
        if(m_a != null) m_a.PropertyChanged += OnAPropertyChanged;
        RaisePropertyChanged("A");
      }
    }
  }

  private void OnAPropertyChanged(object sender, PropertyChangedEventArgs e) {
    RaisePropertyChanged("A." + e.PropertyName);
  }

}


B b = new B();
b.PropertyChanged += (s, e) => { Console.WriteLine(e.PropertyName); };
b.A.Name = "Blah"; // Will print "A.Name"
于 2013-04-09T14:47:47.630 に答える
1

ここで行う最善の方法は、ModelViewModelの概念を分離することです。

ViewModelよりも平らなオブジェクトを使用することでModel、このシナリオを回避できます。Automapperのような自動マッピング ツールを使用すると、Modelを に、ViewModelまたはその逆にマッピングできます。

https://github.com/AutoMapper/AutoMapper/wiki/Flattening

class MyDatViewModel : INotifyPropertyChanged
{
    public string Str
    {
        // ... Get Set
    }

    public int NestedObjNum
    {
        // ... Get set
    }
}

// Configure AutoMapper

Mapper.CreateMap<MyDat, MyDatViewModel>();

// Perform mapping

MyDatViewModel viewModel = Mapper.Map<MyDat, MyDatViewModel>(someData);
于 2013-04-09T15:40:33.377 に答える