2009-03-24 13 views
1

Ich möchte den ViewModel-Teil von WPFs MVVM-Muster implementieren, ohne WPF-Assemblys zu referenzieren. Der problematische Teil ist das Befehlsrouting, bei dem ViewModels Eigenschaften des Typs ICommand implementieren muss, damit Befehlsbindungen funktionieren können.Eigenschaften in .NET-Klassen nach der Kompilierung einspeisen

Nun kann ich die ICommand vermeiden und einfach die Eigenschaften als object deklarieren. Alles funktioniert immer noch, also das ist es. Aber was mich stört ist, Ich muss sie immer noch deklarieren, und ich will wirklich nicht, weil sie wie Kessel Platte Code fühlen.

Meine Viewmodels aussehen derzeit wie folgt aus:

public class HelloWorldViewModel : ViewModel 
{ 
    [BoundProperty] 
    public string Name { get; set; } 

    [CommandHandler("SayHello")] 
    public bool CanSayHello() 
    { 
     return Name != "" && Name != null; 
    } 

    [CommandHandler("SayHello")] 
    public void SayHello() 
    { 
     View.ShowMessage("Hello, {0}!", Name); 
    } 

    public object SayHello { get; private set; } 
} 

Die CommandHandlerAttribute ermöglicht Laufzeit Entdeckung von Befehlshandler (ein Action und eine optionale Func<bool>), während die BoundPropertyAttribute wirklich ein Aspekt ist, die sich in das Eigentum spritzt Setter und Anrufe INotifyPropertyChanged. Ich begleite dies mit einer Kompilierzeit IL Weaver.

Im Idealfall möchte ich auch die letzte Zeile (die SayHello-Eigenschaft) implizit machen. Es würde keinen Sinn haben, es in der Quelle zu haben, wenn es nicht die Anforderung von WPF wäre.

Also, natürlich, ich denke, den CommandHandlerAttribute Aspekt der Verwendung des notwendigen IL in der Klasse und im Wesentlichen Schaffung Eigentum Post kompiliert zu injizieren. Dies ist ziemlich schwierig, obwohl ein guter IL-Weber (wie PostSharp) einen langen Weg gehen kann, um es einfacher zu machen.

Bevor ich mich auf diese Reise beginne, würde ich gerne hören, was Sie alle über meinen Ansatz denken. Ist es gesund? Gibt es einen besseren Weg? Wie würdest du das machen?

Antwort

3

Das klingt für mich bei weitem zu schlau. Es passiert zu viel "Magie". Insbesondere mag ich die magischen Zeichenfolgen und andere Aspekte Ihres CommandHandlerAttribute nicht. Das heißt, wenn ich diesen Weg gehen würde, würde ich etwas verwenden, das dem EventAggregator ähnlich ist, aber für Befehle. IOW, SayHello würde überhaupt nicht auf Ihrem ViewModel existieren. Was auch immer magic die Befehlsbindungen zu SayHell() und CanSayHello() erstellt, würde stattdessen den Befehl im globalen CommandAggregator finden. Solange wir dafür magische Zeichenfolgen verwenden, können die Befehle im CommandAggregator träge erstellt werden, so dass auf Ihrem Teil keine "Boiler Plate" -Codierung erforderlich ist. Alles, was übrig bleibt, ist eine XAML-Magie (Markup-Erweiterung) zu erstellen, um den Befehl auf der ICommandSource anzugeben.

<Button Command="{my:AggregateCommand SayHello}"/> 
0

Meine persönliche Meinung ist, dass dies interessant ist, aber ich würde es im Allgemeinen vermeiden.

Die Vermeidung von Kesselplattencode (oder Code, der sich wie Kesselplattencode anfühlt) hat Konsequenzen. Es scheint eine gute Idee zu sein, da Sie die Dinge nicht ständig neu eingeben, aber auf lange Sicht machen Sie es weniger lesbar und verständlich.

Ich persönlich versuche, nur gute Code-Vorlagen einzurichten, um den Kesselblech-Code für mich einzufügen, und wickle es in Regionen, damit ich es im Quellcode verstecken kann. Die 30 Sekunden, die man braucht, um in diesem Fall eine Datei mit einer Kesselplatte auszufüllen, sind weniger schmerzhaft (für mich) als die zwei Stunden, die ich verbringe, zwei Jahre später, wenn ich versuche, den Code zu verstehen, oder noch schlimmer, die zwei Wochen sonst verbringt sie zwei Jahre später, wenn sie versuchen, meinen Code zu verstehen ....

1

Einige Zeit nach dem Spielen mit Prism, aber bevor ich die MVVM-Sachen scheinen würde, kam ich auf eine Strategie, die ich immer noch denke etwas Gültigkeit:

Ich habe eine Implementierung der ICommand-Schnittstelle basierend auf Reflexion erstellt. Der Konstruktor hat ein Zielobjekt und einen Operationsnamen akzeptiert. Bei der Reflektion suchte der Code nach einer Methode mit dem Namen "[operation]", einer Eigenschaft oder Methode mit dem Namen "Can [operation]" oder "[operation] Enabled" und einem Event mit dem Namen "Can [operation] Changed" oder "[ Operation] Aktiviert Geändert ". Nur die erste wurde benötigt, aber die reflektierte Methode/Eigenschaft/Ereignis wurde mit einer ziemlich einfachen Implementierung der ICommand-Schnittstelle verdrahtet.

Ich habe dann eine Implementierung von IValueConverter erstellt, die eine Instanz der vorherigen Klasse erstellen würde, wobei der Wert übergeben wird, der als Zielobjekt konvertiert werden soll, und der Parameter des Konverters der Operationsname ist.

Angesichts der oben genannten Komponenten konnte ich dann zum Beispiel die Eigenschaft Command einer Schaltfläche direkt an die Quelle der Operation binden (und den Konverter angeben) und die CommandParameter-Eigenschaft der Schaltfläche auf den Namen der Operation festlegen . Auf diese Weise erhielt ich eine deklarative Befehlsbindung, ohne dass die Befehlsquelle ein fleischliches Wissen über irgendetwas WPF hatte.

+0

Ich mag es nicht mit „Magie“ Methoden- und Eigenschaftsnamen Reflexion. Ich habe zu oft in der Vergangenheit gefunden, ich benenne eine Methode um und dann unerwartetes Bit der Benutzeroberfläche hat aufgehört zu arbeiten, ohne dass der Compiler mir einen Fehler gibt. Wie auch immer ich mag dein grundlegendes Design, was ist mit Lambda-Ausdrücken für die Verkabelung? –

+0

Ich mag die Idee eines Lambdas sehr. Ich möchte vielleicht immer noch die "aktivierte" Eigenschaft/das Ereignis per Reflektion entdecken (zumindest standardmäßig), da dies die Schnittstelle vereinfachen würde. –

0

Der beste Weg in Ihrem Fall ist Proxy oder Decorator Muster denke ich. Sie können Entitäten auf niedriger Ebene verwenden, die zur Laufzeit mit UI/WPF-Stuff-Membern umhüllt/dekoriert sind. Dies ist die einfachste, aber dennoch effiziente Möglichkeit, Zeit zu sparen und sich nicht mit Frameworks, Injektionen usw. zu beschäftigen.

Die einzige Sache ist, dass Sie eine kleine Infrastruktur entwerfen müssen, um Ihre Entitäten mit geeigneten Dekoratoren zu umhüllen.

2

i Ratschläge, die Sie zu sehen, wie dies umgesetzt wurde, und es wird dazu beitragen:

"Kind Of Magic" Mühelos INotifyPropertyChanged

[http://visualstudiogallery.msdn.microsoft.com/ d5cd6aa1-57a5-4aaa-a2be-969c6db7f88a] [1]

als Beispiel für die Zugabe zu einer Eigenschaft:

[Magic] 
public string Name { get { return _name; } set { _name = value; } } 
string _name; 

Ein weiteres Beispiel dafür an alle Klasseneigenschaften hinzu:

[Magic] 
public class MyViewModel: INotifyPropertyChanged 
{ 
    public string Name { get; set; } 
    public string LastName { get; set; } 
    ..... 
}