C# [MVVM]Datenaustausch zwischen ViewModels, View schließen

ML89

Lt. Junior Grade
Registriert
Apr. 2014
Beiträge
440
Guten Morgen,

derzeit beschäftige ich mich mit dem MVVM-Schema und komme damit auch eigentlich gut zu recht und empfinde es auch als hilfreich. Nun möchte ich ein zweites Fenster vom Hauptfenster meines Programms öffnen. Normalerweise soll das ViewModel nichts von anderen ViewModels oder gar dem View wissen, aber ich prinzipiell würde es mich bei dieser Anwendung nicht stören das zweite Fenster aus dem ViewModel des Hauptfensters zu öffnen:

Code:
Window secondWindow = new Window();
secondWindow.Show();

In diesem zweiten Fenster sollen Einstellungen gemacht werden, die natürlich im Hauptfenster verwendet werden sollen. Ich möchte daher Daten zwischen den beiden ViewModel austauschen. Was ich biher gemacht habe:

Code:
SecondWindowViewModel secondWindowViewModel = new SecondWindowViewModel();
secondWindowViewModel.Property = this.Property;
Window secondWindow = new Window();
secondWindow.DataContext = secondWindowViewModel;
secondWindow.Show();

Das funktioniert auch eigentlich ganz prächtig. Meine Frage hier wäre lediglich, ob es da eine elegantere Vorgehensweise gibt.

Mein zweites Problem besteht quasi nach dem Öffnen des zweiten Windows. Wenn ich schließen möchte steht mir ja erstmal nicht mehr this.Close() zur Verfügung. Wie kann ich also das Fenster schließen und vor allem so schließen, dass ich es danach wieder aufrufen kann?
 
Hallo,
ich würde das eher in die Code behind legen, als in das ViewModel. DENN: Von der Theorie her könnte ein ViewModel auch in einer Webapplikation verwendet werden. Der Browser könnte das Fenster nicht öffnen und es würde wahrscheinlich scheppern. Gleiches Problem besteht ja bei Fehlermeldungen per MessageBox. Diese sollte auch nicht aus einem ViewModel aufgerufen werden...aus gleichem Grund.

Mit MVVM lässt sich vieles entkoopeln, aber leider nicht alles...bzw. nicht alles sauber.

greetz
hroessler
 
Okay. Dann ergibt sich aber wieder ein Hinderniss. Ich möchte, dass vor dem Schließen des Fenster eine Aktion ausgeführt wird. Da die gesamte "Intelligenz" im ViewModel liegt, muss ich es aufrufen. Allerdings gibt mir die Eigenschaft DataContext nichts zurück.
 
Moin, ich hab dir mal ein Beispiel beigelegt.

Das Prinzip ist so, dass im MainViewModel ein Event gefeuert wird. Das View fängt dieses Event ab und öffnet daraufhin über die ConfigurationWindowAction ein neues Fenster mit dem ConfigurationViewModel welches das MainViewModel übergeben hat. Im MainViewModel wird nun darauf gewartet, dass das Fenster wieder geschlossen wird (s. Zusatz.). Im ConfigurationWindow ist hinterlegt, dass bei dem Event "Closing" das Command "FinishCommand" des ViewModels aufgerufen wird. Wird es geschlossen wird der aus dem neuen Fenster eingegebene Text im MainWindow angezeigt. So könnte eine entkoppelte Art und Weise einer Implementierung aussehen.

Zusatz:
Die synchrone Bearbeitung funktioniert nur, weil die ConfigurationWindowAction das neue Fenster als modalen Dialog öffnet. Wird es als nicht modales Fenster geöffnet, muss die Action sowie IRequest erweitert werden, dass es so eine Art Callback o. ä. gibt. Das erfordert etwas mehr Hirnschmalz und eine bessere Implementierung.
 

Anhänge

Danke dir! Ich glaube ich steige dadurch. Nur eine Frage bleibt noch: Wie kann ich von zweiten Fenster aus übe reinen Butten die Form schließen? Also ich würde gerne Schaltflächen mit "OK" und Abbrechen"machen". Beim Klicken soll halt etwas passieren und dann die Form geschlossen werden. Das ist mir noch nicht ganz klar. (Außer natürlich ich nehme den Code-Behind)
 
Zuletzt bearbeitet:
Dann würde ich noch zwei Commands im ConfigurationViewModel hinzufügen. AcceptCommand und CancelCommand (o. ä). Einmal für OK und einmal für Abbrechen. Diese setzen dann im ConfigurationViewModel noch eine Variable auf true bzw. false. Damit du das View vom ViewModel schließen kannst, würde ich dem IRequest noch eine Property verpassen.
Code:
 Action Finish {get; set;}
Diese setzt du dann in der ConfigurationWindowAction, damit dann das Fenster vom ViewModel geschlossen werden kann. Etwa so
Code:
Window window = new ConfigurationView() {
    DataContext = requestArgs.Request
};
request.Finish = () => window.Close();
window.Show();

Im ViewModel kannst du dann in den Commands die Methode Finish aufrufen. Die schließt dann in diesem Fall das Fenster.

Hab das jetzt vom Handy geschrieben, kannst also nicht 1:1 übernehmen. Fehlerfrei wird es wahrscheinlich auch nicht sofort sein. Aber vom Prinzip funktioniert es so.
 
Zuletzt bearbeitet:
Puh. Das ist für mich ein ganz schönes "Bröckchen" :p. Was ich nicht verstanden habe ist, wie dein Hauptfenster auf das Schließen des SubWindows wartet. Und mit using System.Windows.Interactivity; habe ich ein problem, denn ich kriege es nicht eingebunden. Gibt es da einen Trick?
 
Es wartet, weil das SubWindow mit der Methode "ShowDialog()" aufgerufen wird. Die blockiert, bis das geöffnete Fenster wieder geschlossen wird.
Für die Bibliothek musst du Expression Blend und/oder das Blend SDK installieren.
 
Ahhh, danke!

Bin wohl doch zu dumm. Das Interface schaut jetzt so aus:

Code:
interface IRequest
{
     ICommand FinishCommand { get; }

     Action Close{ get; set; }
}

soweit, so gut. Das ViewModel:
Code:
class ConfigurationViewModel : IRequest
{
    public ConfigurationViewModel()
    {
        FinishCommand = new SimpleCommand(Finish);
    }

    public ICommand FinishCommand { get; }
        
    public string Text { get; set; }

    ICommand IRequest.FinishCommand => throw new NotImplementedException();

    Action IRequest.Close { get => throw new NotImplementedException(); set => throw new    NotImplementedException(); }

    private void Finish()
    {
         // do something when finishing request
    }
}

Ich weiß nicht so recht, wie ich nit dem Action.IRequest Close umgehen soll. Es sieht aus wie eine Property, aber wie kann ich sie aufrufen?

EDIT: Habe es hinbekommen. Allerding schmeißt er dann eine System.InvalidOperation bei close.
 
Zuletzt bearbeitet:
"Visibility kann nicht auf "Visible" festgelegt und Show, ShowDialog, Close oder WindowInteropHelper.EnsureHandle können nicht aufgerufen werden, während ein Window geschlossen wird."
 
Schon mal was von Prism Framework gehört, oder Dependency Injection, Unity oder MEF?

Damit könntest du dich beschäftigen und deine Arbeitszeit um ein vielfaches verkürzen.

Setzt aber WPF voraus oder Xamarin Forms. Also setzt es nicht aber, wenn man grafische Anwendungen programmieren will schon xD
 
Zuletzt bearbeitet:
Nein. Ich denke, dass ich das Ganze fürs Erste mal ruhen lassen für diese eine spezielle Anwendung. Es scheint ja nicht so trivial zu sein mit MVVM - oder ich denke schlicht falsch und versuche etwas unnötig schwer umzusetzen.
 
Viele Wege führen nach Rom. Es gibt Wege die koppeln den ganzen Kram extrem lose, aber es gibt auch Wege, die das mit mehr Kopplung lösen. Wenn du deine Lösung in mein Beispiel integrierst und es einmal bereitstellst, kann ich mir das einmal anschauen.
 
Setze jetzt so etwas wie weinen ViewModelLocator ein. Also eine statische Klasse, die sowohl View als auch ViewModel kennt. Fertig. Ich habe mir ein Webinar zu Prism angeschaut und ich denke, dass ich mir noch etwas Zeit lassen sollte - und kann, bis ich mich mit diesem Thema weiter befasse.
 
War ja auch nur ein Vorschlag, weil das Prism Framework gerade genau das tut, nämlich eine lose Kopplung ermöglicht, ohne Stunden sich selbst darüber den Kopf zu zerbrechen.

 
Mir erscheint das alles recht umständlich. Gehören deine Einstellungen nicht in das Model? Von da aus kommen auch beide ViewModels dran.
 
Also erstmal habe ich aus diversen Gründen eine Abneigung gegen Frameworks jeglicher Art :freak: Warum? Ich bin der Meinung, dass man grundlegende Sachen schon selber können sollte - nicht nur auf das Programmieren bezogen. PRISM selber ist in dem Sinne auch nur eine Erweiterung. Mein CommandHandler entspricht z.B. dem von PRISM - kein Problem und eine sinnige Erweiterung.

Das Window-Close-Problem konnte ich mit meinem ViewModelLocator umgehen. Für meine spezielle Anwendung gerade stellt das auch kein Problem dar, wenn es nicht optimal funktioniert. Nur dadurch, dass ich ihn geschrieben habe, habe ich begriffen, was da passiert. PRISM stellt meiner Meinung nach auch keine endgültige Lösung dar, wie hier zu sehen ist. Bei Minute 25 sieht man, wie aufgrund der ObservesProperty-Methode das Programm deutlich wächst. Die berechtigte Frage, wass denn passiert, wenn man mehrere solcher Befehle hat und das Ganze damit noch etwas unübersichtlicher wird wird dann damit beantwortet, dass der Programmierer dann wohl etwas nicht richtig macht. Ich halte diese Aussage für fragwürdig.

Mir erscheint das alles recht umständlich. Gehören deine Einstellungen nicht in das Model? Von da aus kommen auch beide ViewModels dran.

Habe ich auch lange drüber nachgedacht. Aber ich will ja an einen Befehl des Views ran. Ich kann zwar quasi immer nach unten gehen, also: [View] -> [ViewModel] ->[DataModel], aber es wird tricky nach oben zu kommen. Das Einzige, was noch irgendwie geht ist ein CloseEvent im ViewModel zu platzieren und das im View dann abzufangen. Das lässt sich auch im XAML über die Triggers aus der System.Windows.Interaction bewerkstelligen. Das entspräche dann auch dem hier:

IC564167.png

Demnach hat auch die ganze, bzw. ein großer Teil Interaktions-Logik im ViewModel stattzufinden. Und wenn ich mit meiner App etwas komplex tue, wird diese Logik auch etwas größer, was mich dann wieder zu meiner Kritik an PRISM führt. Klar, wenn ich irgendeine Handy-App mache, die nur drei Buttons hat, dann ist das kein Problem.

Was ich mich jetzt halt grundsätzlich frage ist, ob ich MVVM wirklich brauche, wenn ich weiß, dass meine Zielplatform der PC ist.
 
Zuletzt bearbeitet:
Dann hast du Prism nicht verstanden.

Prism unterstützt dich darin eine lose Kopplung hin zu bekommen ohne zu.B. die Interaktion Logik in der jeweiligen Code behind der view zu platzieren des weiteren musst du nichts weiter tun um das ViewModel mit der View zu koppeln da der ViewModelLocator das für dich tut wenn man sich an bestimmte Konventionen hält im Verbund mit Dependency Injektion muss man sich dann auch keine Gedanken machen wo ich welche klasse instanziere da es durch den MEF oder Unity Container zentral erreichbar ist überall gerade bei komplexen Anwendungen finde ich ist Prism ein Muss und erleichtert einem viel Denkarbeit.

Man kann dennoch auch unter Verwendung von Prism völlig dran vorbei programmieren.

Den Standpunkt das man grundlegendes selber können alle finde ich auch gut, aber warum das Rad neu erfinden wenn man ein top light weight Framework hat was einem Arbeit ab nimmt.

Schau dir doch mal die vielen Samples von Prism auf github an. Ich denke da findest du fast alles was man so braucht und wissen muss.

Darf ich fragen was diese Anwendung können soll?
 
ML89 schrieb:
Ich kann zwar quasi immer nach unten gehen, also: [View] -> [ViewModel] ->[DataModel], aber es wird tricky nach oben zu kommen. Das Einzige, was noch irgendwie geht ist ein CloseEvent im ViewModel zu platzieren und das im View dann abzufangen.
Das ist völlig beabsichtigt, weil das ViewModel im Idealfall den View überhaupt nicht kennt und mit ihm nur indirekt über Data Bindung, Commands und Events kommuniziert. Das ViewModel muss auch ohne View funktionieren.
Die ViewModels untereinander kommunizieren entweder direkt oder besser indirekt über einen Mediator.

ML89 schrieb:
Demnach hat auch die ganze, bzw. ein großer Teil Interaktions-Logik im ViewModel stattzufinden. Und wenn ich mit meiner App etwas komplex tue, wird diese Logik auch etwas größer, was mich dann wieder zu meiner Kritik an PRISM führt. Klar, wenn ich irgendeine Handy-App mache, die nur drei Buttons hat, dann ist das kein Problem.
Es ist aber genau das Ziel von MVVM. Die Interaktionslogik sollte soweit wie möglich im ViewModel stattfinden, weil sie dort z. B. viel einfacher testbar ist.
Wenn ein ViewModel zu groß wird muss man es aufteilen. Ein ViewModel kann durchaus "Sub-ViewModels" haben.

ML89 schrieb:
Was ich mich jetzt halt grundsätzlich frage ist, ob ich MVVM wirklich brauche, wenn ich weiß, dass meine Zielplatform der PC ist.
Die Frage ob man MVVM einsetzt oder nicht hat nichts mit der Zielplattform zu tun sondern ist letzen Endes eine Geschmacksfrage.
Wenn man aber Wert auf loose Kopplung und Testbarkeit legt und die passenden technischen Möglichkeiten hat (Data Binding etc.) kommt man an MVVM meiner Meinung nach kaum vorbei.
 
Zurück
Oben