2009-05-14 5 views
12

Wir haben viele unveränderliche Wertobjekte in unserem Domänenmodell, ein Beispiel dafür ist eine Position, die durch eine Breite, Länge & Höhe definiert ist.Wie kann ich unveränderliche Objekte in WPF bearbeiten, ohne Code zu duplizieren?

/// <remarks>When I grow up I want to be an F# record.</remarks> 
public class Position 
{ 
    public double Latitude 
    { 
     get; 
     private set; 
    } 

    // snip 

    public Position(double latitude, double longitude, double height) 
    { 
     Latitude = latitude; 
     // snip 
    } 
} 

Der offensichtliche Weg Bearbeitung von einer Position zu ermöglichen, ist eine Ansichtsmodell zu bauen, die Getter und Einrichter, sowie eine ToPosition() Methode der validierte unveränderliche Position Instanz zu extrahieren, aufweist. Obwohl diese Lösung in Ordnung wäre, würde sie viel doppelten Code ergeben, insbesondere XAML.

Die fraglichen Wertobjekte bestehen aus drei bis fünf Eigenschaften, die in der Regel einige Varianten von X, Y, Z & Hilfsteilen sind. In Anbetracht dessen hatte ich überlegt, drei ViewModels zu erstellen, um die verschiedenen Möglichkeiten zu behandeln, wobei jedes ViewModel Eigenschaften für den Wert jeder Eigenschaft sowie eine Beschreibung für jedes Etikett (z. B. "Latitude") verfügbar machen müsste.

Weiter geht es, als ob ich es zu einem allgemeinen ViewModel vereinfachen könnte, das mit N Eigenschaften umgehen und alles durch Reflektion verbinden kann. So etwas wie ein Eigenschaftsraster, aber für unveränderliche Objekte. Ein Problem mit einem Eigenschaftenraster ist, dass ich möchte in der Lage sein, um das Aussehen zu ändern, so dass ich Etiketten und Textfelder haben kann wie zum Beispiel:

Latitude: [  32 ] <- TextBox 
Longitude: [  115 ] 
Height:  [  12 ] 

oder legen Sie sie in einem Datagrid wie:

Latitude | Longitude | Height 
     32   115   12 

Also meine Frage ist:

Können Sie an eine elegante Möglichkeit denken, dieses Problem zu lösen? Gibt es Bibliotheken, die das machen oder Artikel über etwas Ähnliches?

ich in erster Linie bin auf der Suche nach:

  • Code-Duplizierung zu
  • leicht minimiert werden, um neue Wert Objekttypen
  • Mögliche mit irgendeiner Art von Validierung zu verlängern hinzufügen

Antwort

2

Ich habe diese alte Frage bei der Untersuchung meiner möglichen Optionen in der gleichen Situation gefunden. Ich dachte, ich sollte es aktualisieren, falls jemand anderes darauf stolpert:

Eine andere Option (nicht verfügbar, als Paul seine Lösung angeboten. NET 4 war noch nicht out) ist die gleiche Strategie zu verwenden, aber statt zu implementieren Verwenden Sie CustomTypeDescriptors, verwenden Sie eine Kombination aus Generika, dynamischen Objekten und Reflektion, um denselben Effekt zu erzielen.

In diesem Fall definieren Sie eine Klasse

class Mutable<ImmutableType> : DynamicObject 
{ 
    //... 
} 

Es Konstruktor eine Instanz der unveränderlichen Art nimmt und einen Delegierten, die eine neue Instanz davon aus einem Wörterbuch, wie in Paul Antwort konstruiert. Der Unterschied besteht jedoch darin, dass Sie TryGetMember und TrySetMember überschreiben, um ein internes Wörterbuch zu füllen, das Sie eventuell als Argument für den Konstruktor-Delegaten verwenden. Sie verwenden Reflektion, um zu überprüfen, ob die einzigen Eigenschaften, die Sie akzeptieren, diejenigen sind, die tatsächlich in ImmutableType implementiert sind.

Leistung weise, ich wette, dass Pauls Antwort ist schneller und beinhaltet keine dynamischen Objekte, die C# -Entwickler in Passungen bekannt sind. Aber die Implementierung für diese Lösung ist auch ein wenig einfacher, weil Typdeskriptoren ein bisschen geheimnisvoll sind.


Hier ist die angeforderte Proof-of-Concept-/Beispielimplementierung:

https://bitbucket.org/jwrush/mutable-generic-example

+0

Dies ist eine nette Lösung, ich bevorzuge es zu Pauls, da es weniger Boilerplate gibt. Natürlich könnten Sie auch Reflektion in seiner Lösung verwenden, um den Programmieraufwand beim Zuordnen von Eigenschaften zum Wörterbuch zu reduzieren. Es gibt tatsächlich keine akzeptierte Antwort, weil ich das Problem auf eine andere Weise gelöst habe und Pauls Antwort 6 Monate oder später nicht bemerkt hatte, nachdem ich die Frage ursprünglich gestellt hatte. Wenn Sie ein praktikables Code-Snippet anbieten würden, das die Leute einfach verwenden könnten, werde ich dies als akzeptierte Antwort markieren. –

0

Können Sie sich einen eleganten Weg vorstellen, dieses Problem zu lösen?

Ehrlich gesagt, tanzen Sie nur um das Problem, aber das Problem selbst nicht erwähnen;).

Wenn ich Ihr Problem richtig errate, dann sollte die Kombination von MultiBinding und IMultiValueConverter den Trick machen.

HTH.

P.S. Übrigens, Sie haben unveränderliche Klasseninstanzen, keine Wertobjekte. Mit Value-Objekten (die unter struct Stichwort beschrieben sind) würden Sie viel mehr tanzen, egal ob es Setter gab oder nicht :).

+2

Ich glaube, Sie ein Missverständnis von Wertobjekten haben. Werttypen sind Strukturen, wie Sie sagen, aber ein Wertobjekt ist nicht dasselbe. Siehe diesen Artikel für weitere Informationen: http://www.losetechies.com/blogs/jimmy_bogard/archive/2008/05/20/entities-value-objects-aggregates-and-roots.aspx –

+1

Könnten Sie vielleicht näher auf Wie kann ich das Problem deutlicher machen? Kurz gesagt, ich habe ein Objekt, das unveränderlich ist, und ich möchte in der Lage sein, Textfelder auf seine gleiche Weise leicht an seine Eigenschaften zu binden, wie ich mit einem veränderbaren Objekt kann. –

+0

Sorry über Wertobjekte - meine Schuld, wusste nicht, dass es eine gibt. Über die Ausarbeitung des Problems - zum Beispiel können Sie eine der Snips zur Verfügung stellen, die Sie generieren möchten. Vielleicht wäre es auch nützlich, wenn Sie einige Meta-Snips Ihres Sehens der idealen generischen Lösung in XAML bereitstellen. – archimed7592

5

Benutzerdefinierte Typenbeschreibungen könnten zur Lösung dieses Problems verwendet werden. Bevor Sie sich an eine Position binden, kann Ihr Deskriptor für den Typ einspringen und Methoden zum temporären Erstellen der Werte bereitstellen. Wenn die Änderungen festgeschrieben werden, kann das unveränderbare Objekt erstellt werden.

Es könnte wie folgt aussehen:

DataContext = new Mutable(position, 
    dictionary => new Position(dictionary["lattitude"], ...) 
); 

Ihre Bindungen immer noch so aussehen kann:

<TextBox Text="{Binding Path=Lattitude}" /> 

Da das Veränderliche Objekt ‚vorgeben‘ Eigenschaften haben wie Lattitude dank seiner TypeDescriptor .

Alternativ können Sie einen Konverter in Ihren Bindungen verwenden und eine Art Konvention treffen.

Ihre Mutable-Klasse würde das aktuelle unveränderbare Objekt und eine Func<IDictionary, object> annehmen, mit der Sie das neue unveränderliche Objekt erstellen können, sobald die Bearbeitung abgeschlossen ist. Ihre Mutable-Klasse würde den Typdeskriptor verwenden, der PropertyDescriptors erstellt, die beim Festlegen das neue unveränderbare Objekt erstellen.

Ein Beispiel dafür, wie Typdeskriptoren Verwendung finden Sie hier:

http://www.paulstovell.com/editable-object-adapter

bearbeiten: Wenn Sie einschränken möchten, wie oft Ihre unveränderliche Objekte erstellt werden, können Sie auch bei BindingGroups aussehen und IEditableObject, das auch von Ihrem Mutable implementiert werden kann.

Verwandte Themen