2010-01-05 5 views
15

ist in einem abstrakten Ansichtsmodell Basis-Klasse sagen Let Ich habe eine einfache alte Eigenschaft wie folgt:Änderungsmitteilung in MVVM Hierarchies

public Size Size 
{ 
    get { return _size; } 
    set 
    { 
     _size = value; 
     OnPropertyChanged("Size"); 
    } 
} 

ich dann eine spezifischere Ansichtsmodell erstellen, von der vorherigen vererben, die enthält die folgende Eigenschaft:

public Rect Rectangle 
{ 
    get { return new Rect(0, 0, _size.Width, _size.Height); } 
} 

, jetzt in einigen View-Klasse I binden an die Rectangle Eigenschaft des oben genannten Ansichtsmodell. Alles funktioniert gut, bis ich die Größe ändere. Wenn Size ändert, Rectangle weiß es nicht, und die Änderung wird nicht an die Ansicht weitergegeben. Und da Rectangle in der Kindklasse ist, kann ich nicht einfach einen OnPropertyChanged("Rectangle") zum Size Setzer hinzufügen.

Stellen Sie sich nun vor, dass ich viele verschiedene Eigenschaften wie Rectangle habe, die alle von Basisklasseneigenschaften abhängen, und dass keine dieser Änderungen propagiert wird. Ich brauche eine leichte und elegante Möglichkeit, Änderungsbenachrichtigungen zu verketten, vorzugsweise eine, die nicht viel Code erfordert und mich nicht dazu zwingt, Abhängigkeitseigenschaften zu verwenden.

Offensichtlich gibt es hier viele hässliche Lösungen - was ich suche ist etwas sauberes und kluges. Es scheint mir, dass dies ein sehr häufiges Szenario wäre, und es scheint mir, dass es eine MVVM-freundliche Art und Weise, dies zu tun, sein könnte.

Antwort

9

Ich habe kürzlich über genau dieses Problem gebloggt. Ich schließe ein [DependsUpon("Size")] Attribut mit dem Rechteck ein. Ich mag diesen Ansatz wirklich, weil er das Abhängigkeits-Wissen mit dem Code hält, der die Abhängigkeit erzeugt, und nicht umgekehrt.

Werfen Sie einen Blick: http://houseofbilz.com/archive/2009/11/14/adventures-in-mvvm----dependant-properties-with-inotifypropertychanged.aspx

+0

Großartige Idee. Ich änderte es ein wenig, um ein Wörterbuch zur Konstruktionszeit (aus Leistungsgründen) zu erstellen, aber das Konzept ist gesund. – Charlie

3

Sie können einfach wie so in den abgeleiteten Ansichtsmodell OnPropertyChanged außer Kraft setzen:

protected override void OnPropertyChanged(string propertyName) { 
    base.OnPropertyChanged(propertyName); 
    if (propertyName == "Size") { 
     base.OnPropertyChanged("Rectangle"); 
    } 
} 

Eine andere Möglichkeit ... Vor einer Weile ich eine ziemlich schöne Ansichtsmodell Basisklasse zusammen, die wie Attribute auf Eigenschaften unterstützt:

Dann sammelt die ViewModel-Basisklasse diese DependsOnAttribute zur Laufzeit und in ihrer OnPropertyChanged-Methode sieht sie im Grunde nur nach, welche anderen Eigenschaften beim Ändern einer Eigenschaft ungültig gemacht werden müssen e tritt auf.

+0

Überschreiben OnPropertyChanged ist definitiv eine Lösung, aber keine elegante. Ich mag Ihren zweiten Vorschlag jedoch besser. – Charlie

+0

Nicht alle Lösungen verlangen nach Eleganz. Das Überschreiben von OnPropertyChanged ist sehr einfach und ich mache das immer noch in vielen Fällen, es sei denn, die Abhängigkeiten werden haarig, was mich zu dem zweiten Ansatz geführt hat. – Josh

+0

Sie haben Recht, aber denken Sie daran, dass ich in meiner Frage ausdrücklich gesagt habe: "Stellen Sie sich nun vor, dass ich viele verschiedene Eigenschaften wie Rectangle habe, die alle von Basisklasseneigenschaften abhängen und dass keine dieser Änderungen propagiert wird." Die Annahme ist also, dass die Hierarchie bereits haarig wird. – Charlie

1

Eine saubere MVVM Weise eine Messenger subscribe/notify-Mechanismus (wie in Josh Smith MvvmFoundation)

Erstellen eines Singletons Messenger irgendwo Objekt zu verwenden, wäre - die Haupt-App-Klasse für diese

immer ein guter Ort ist
public partial class App : Application 
{ 
    private static Messenger _messenger; 
    public static Messenger Messenger 
    { 
     get 
     { 
      if (_messenger == null) 
      { 
       _messenger = new Messenger(); 
      } 
      return _messenger; 
     } 
    } 
} 

In der Größe Setter von der Basisklasse, benachrichtigen Änderungen:

public Size Size 
{ 
    get { return _size; } 
    set 
    { 
     _size = value; 
     OnPropertyChanged("Size"); 

     App.Messenger.NotifyColleagues("SIZE_CHANGED"); 
    } 
} 

Jetzt können Sie Ihre geerbt anzeigen lassen Modelles für diese Veränderungen hören, und erhöhen Property Veranstaltungen finden als angemessen ...

public MyViewModel : MyViewModelBase 
{ 
    public MyViewModel() 
    { 
     App.Messenger.Register("SIZE_CHANGED",() => OnPropertyChanged("Rectangle")); 
    } 
} 

Natürlich - Sie so viele Abonnements zu dieser Nachricht hinzufügen können, wie Sie benötigen - eine für jede Eigenschaft, die Änderungen informiert werden muss zurück zu die Aussicht...

this helps :)

+0

Das tut genau das gleiche wie das Überschreiben von OnPropertyChanged für das Kind im Elternteil, nicht sicher, ob es wirklich sauberer ist. – AwkwardCoder

+0

Das ist keine schlechte Idee, aber ich will etwas leichteres. – Charlie

+0

Der Vorteil der Verwendung eines Messenger-Ansatzes liegt möglicherweise darin, dass Sie tief verschachtelte Hierarchien haben, in denen es ungünstig und möglicherweise ineffizient wäre, die Eigenschaftsänderungsereignisse zu verketten. – jpierson

0

vielleicht weil im ein VB Kerl, aber in Ihrem Rectangle Code sieht es aus wie Sie die private _size Erklärung anstelle der öffentlichen Size-Eigenschaft zugreifen, die das OnPropertyChanged Ereignis nicht ausgelöst würden zu alarmieren die Aussicht.

Auch ich kann von der Basis sein, aber sollte Rectangle nicht ein tatsächliches Objekt sein, während Größe eine Eigenschaft dieses Objekts ist? Vielleicht tust du das. Einige C# -Methoden sind mir immer noch fremd.

+0

Ich verwende die private _size-Deklaration, aber es ist irrelevant. Das ist ein Getter, kein Setter. Die Ansicht muss benachrichtigt werden, dass sich Rechteck ändert, wenn Größe geändert wird, nicht wenn auf Rechteck zugegriffen wird. – Charlie

4

ich Josh Smith PropertyObserver verwenden, die Sie von seiner MVVM Stiftung Bibliothek bei http://mvvmfoundation.codeplex.com/ bekommen.

Verbrauch:

_viewmodel_observer = new PropertyObserver<OtherViewModel>(_OtherViewModel) 
    .RegisterHandler(m => m.Size, m => RaisePropertyChanged(Rectangle); 

Brians Attribut Ansatz ist auch nett. Eine Sache, die ich an PropertyObserver mag, ist, dass ich beliebigen Code ausführen kann; Erlaubt mir, Bedingungen zu überprüfen, die dazu führen können, dass ich die Raise vermeiden oder alle anderen Aktionen durchführen kann.