Wie sollte das ViewModel das Formular schließen?

Ich versuche WPF und das MVVM-Problem zu lernen, habe aber einen Haken gefunden. Diese Frage ist ähnlich, aber nicht ganz so wie diese (handling-dialogs-in-wpf-with-mvvm) …

Ich habe ein “Login” -Formular, das mit dem MVVM-Muster geschrieben wurde.

Dieses Formular enthält ein ViewModel, das den Benutzernamen und das Kennwort enthält, die mit normalen Datenbindungen an die Ansicht im XAML gebunden sind. Es hat auch einen “Login” -Befehl, der an den “Login” -Button des Formulars gebunden ist, und zwar mittels normaler Datenbindung.

Wenn der Befehl “Login” ausgetriggers wird, ruft er eine function im ViewModel auf, die ausgeht und Daten über das Netzwerk zur Anmeldung sendet. Wenn diese function ausgeführt wird, gibt es zwei Aktionen:

  1. Die Anmeldung war ungültig – wir zeigen nur eine MessageBox und alles ist in Ordnung

  2. Die Anmeldung war gültig, wir müssen das Anmeldeformular schließen und es als ” DialogResult ” zurückgeben.

Das Problem ist, dass das ViewModel nichts über die tatsächliche Ansicht weiß, also wie kann es die Ansicht schließen und ihr mitteilen, dass sie ein bestimmtes DialogResult zurückgeben soll ?? Ich könnte etwas Code in den CodeBehind stecken und / oder die View durch das ViewModel leiten, aber das scheint, als würde es den ganzen Punkt von MVVM komplett besiegen …


Aktualisieren

Am Ende habe ich nur die “Reinheit” des MVVM-Musters verletzt und die View ein Closed Ereignis veröffentlichen lassen und eine Close Methode Close . Das ViewModel würde dann einfach view.Close . Die Sicht ist nur über eine Schnittstelle bekannt und über einen IOC-Container verdrahtet, so dass keine Testbarkeit oder Wartbarkeit verloren geht.

Es scheint ziemlich albern zu sein, dass die angenommene Antwort bei -5 Stimmen liegt! Während ich mir der guten Gefühle bewusst bin, die man bekommt, wenn ich ein Problem löse, während ich “rein” bin, bin ich sicher nicht der Einzige, der 200 Zeilen von Ereignissen, Befehlen und Verhaltensweisen denkt, um eine einzeilige Methode zu vermeiden Der Name “Muster” und “Reinheit” ist ein bisschen lächerlich ….

Ich wurde von Thejuan’s Antwort inspiriert, eine einfachere Anlage zu schreiben. Keine Stile, keine Auslöser; Stattdessen können Sie dies tun:

  

Das ist fast so sauber, als hätte das WPF-Team es richtig gemacht und DialogResult überhaupt zu einer Abhängigkeitseigenschaft gemacht. Einfach einen bool? DialogResult bool? DialogResult Eigenschaft bool? DialogResult in Ihrem ViewModel und die Implementierung von INotifyPropertyChanged und voilà, Ihr ViewModel kann das Fenster schließen (und sein DialogResult festlegen), indem Sie einfach eine Eigenschaft festlegen. MVVM wie es sein sollte.

Hier ist der Code für DialogCloser:

 using System.Windows; namespace ExCastle.Wpf { public static class DialogCloser { public static readonly DependencyProperty DialogResultProperty = DependencyProperty.RegisterAttached( "DialogResult", typeof(bool?), typeof(DialogCloser), new PropertyMetadata(DialogResultChanged)); private static void DialogResultChanged( DependencyObject d, DependencyPropertyChangedEventArgs e) { var window = d as Window; if (window != null) window.DialogResult = e.NewValue as bool?; } public static void SetDialogResult(Window target, bool? value) { target.SetValue(DialogResultProperty, value); } } } 

Ich habe das auch in meinem Blog gepostet.

Aus meiner Sicht ist die Frage ziemlich gut, da der gleiche Ansatz nicht nur für das “Login” -Fenster, sondern für jede Art von Fenster verwendet wird. Ich habe viele Vorschläge überprüft und keine ist in Ordnung für mich. Bitte überprüfen Sie meinen Vorschlag, der aus dem MVVM Design Pattern Artikel entnommen wurde.

Jede ViewModel-class sollte von WorkspaceViewModel erben, das über das RequestClose Ereignis und die CloseCommand Eigenschaft des ICommand Typs verfügt. Die Standardimplementierung der CloseCommand Eigenschaft wird das RequestClose Ereignis RequestClose .

Um das Fenster geschlossen zu bekommen, sollte die OnLoaded Methode Ihres Fensters außer Kraft gesetzt werden:

 void CustomerWindow_Loaded(object sender, RoutedEventArgs e) { CustomerViewModel customer = CustomerViewModel.GetYourCustomer(); DataContext = customer; customer.RequestClose += () => { Close(); }; } 

oder OnStartup Methode Ihrer App:

  protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e); MainWindow window = new MainWindow(); var viewModel = new MainWindowViewModel(); viewModel.RequestClose += window.Close; window.DataContext = viewModel; window.Show(); } 

Ich denke, dass die Implementierung des RequestClose Ereignisses und der CloseCommand Eigenschaft im WorkspaceViewModel ziemlich klar ist, aber ich werde zeigen, dass sie konsistent sind:

 public abstract class WorkspaceViewModel : ViewModelBase // There's nothing interesting in ViewModelBase as it only implements the INotifyPropertyChanged interface { RelayCommand _closeCommand; public ICommand CloseCommand { get { if (_closeCommand == null) { _closeCommand = new RelayCommand( param => Close(), param => CanClose() ); } return _closeCommand; } } public event Action RequestClose; public virtual void Close() { if ( RequestClose != null ) { RequestClose(); } } public virtual bool CanClose() { return true; } } 

Und der Quellcode des RelayCommand :

 public class RelayCommand : ICommand { #region Constructors public RelayCommand(Action execute, Predicate canExecute) { if (execute == null) throw new ArgumentNullException("execute"); _execute = execute; _canExecute = canExecute; } #endregion // Constructors #region ICommand Members [DebuggerStepThrough] public bool CanExecute(object parameter) { return _canExecute == null ? true : _canExecute(parameter); } public event EventHandler CanExecuteChanged { add { CommandManager.RequerySuggested += value; } remove { CommandManager.RequerySuggested -= value; } } public void Execute(object parameter) { _execute(parameter); } #endregion // ICommand Members #region Fields readonly Action _execute; readonly Predicate _canExecute; #endregion // Fields } 

PS Behandle mich nicht schlecht für diese Quellen! Hätte ich sie gestern gehabt, hätte mich das ein paar Stunden gerettet …

PPS Alle Kommentare oder Vorschläge sind willkommen.

Ich habe angehängtes Verhalten verwendet, um das Fenster zu schließen. Binden Sie eine “signal” -Eigenschaft an Ihr ViewModel an das angefügte Verhalten (ich verwende tatsächlich einen Trigger). Wenn es auf true gesetzt ist, schließt das Verhalten das Fenster.

http://adammills.wordpress.com/2009/07/01/window-close-from-xaml/

Es gibt viele Kommentare, in denen die Vor- und Nachteile von MVVM diskutiert werden. Für mich stimme ich Nir zu; Es geht darum, das Muster angemessen zu verwenden, und MVVM passt nicht immer. Die Leute scheinen bereit zu sein, alle wichtigen Prinzipien des Software-Designs zu opfern, um es an MVVM anzupassen.

Das heißt, .. ich denke, Ihr Fall könnte eine gute Passform mit ein wenig Refactoring sein.

In den meisten Fällen, die mir bekannt sind, können Sie mit WPF OHNE mehrere Window s auskommen. Vielleicht könnten Sie versuchen, Frame s und Page s anstelle von Windows mit DialogResult s zu verwenden.

In meinem Fall wäre mein Vorschlag, dass LoginFormViewModel den LoginCommand und wenn die Anmeldung ungültig ist, setze eine Eigenschaft auf LoginFormViewModel auf einen geeigneten Wert ( false oder einen bestimmten Enum Wert wie UserAuthenticationStates.FailedAuthentication ). Sie würden das gleiche für eine erfolgreiche Anmeldung tun ( true oder einen anderen Enum-Wert). Sie würden dann einen DataTrigger der auf die verschiedenen Benutzerauthentifizierungsstatus reactjs und einen einfachen Setter , um die Source Eigenschaft des Frame zu ändern.

Wenn Sie Ihr Login-Fenster zurückgeben, geben Sie ein DialogResult , bei dem Sie verwirrt sind. dass DialogResult eine Eigenschaft Ihres ViewModel ist. In meiner, zugegebenermaßen begrenzten Erfahrung mit WPF, wenn sich etwas nicht richtig anfühlt, weil ich normalerweise daran denke, wie ich in WinForms das Gleiche gemacht hätte.

Ich hoffe, das hilft.

Angenommen, Ihr Anmeldedialog ist das erste Fenster, das erstellt wird. Versuchen Sie dies in Ihrer LoginViewModel-class:

  void OnLoginResponse(bool loginSucceded) { if (loginSucceded) { Window1 window = new Window1() { DataContext = new MainWindowViewModel() }; window.Show(); App.Current.MainWindow.Close(); App.Current.MainWindow = window; } else { LoginError = true; } } 

Die Art, wie ich damit umgehen würde, ist, einen Event-Handler in meinem ViewModel hinzuzufügen. Wenn der Benutzer erfolgreich angemeldet war, würde ich das Ereignis auslösen. In meiner Ansicht würde ich an dieses Ereignis anschließen und wenn es gefeuert würde ich das Fenster schließen.

Dies ist eine einfache und saubere Lösung – Sie fügen dem ViewModel ein Ereignis hinzu und weisen das Fenster an, sich selbst zu schließen, wenn dieses Ereignis ausgetriggers wird.

Weitere Details finden Sie in meinem Blogpost, Fenster von ViewModel schließen .

Hier ist, was ich ursprünglich getan habe, was funktioniert, aber es scheint ziemlich langatmig und hässlich (global static alles ist nie gut)

1: App.xaml.cs

 public partial class App : Application { // create a new global custom WPF Command public static readonly RoutedUICommand LoggedIn = new RoutedUICommand(); } 

2: LoginForm.xaml

 // bind the global command to a local eventhandler  

3: LoginForm.xaml.cs

 // implement the local eventhandler in codebehind private void OnLoggedIn( object sender, ExecutedRoutedEventArgs e ) { DialogResult = true; Close(); } 

4: LoginFormViewModel.cs

 // fire the global command from the viewmodel private void OnRemoteServerReturnedSuccess() { App.LoggedIn.Execute(this, null); } 

Später entfernte ich dann diesen ganzen Code und ließ das LoginFormViewModel die Close-Methode in seiner Ansicht aufrufen. Es wurde viel schöner und leichter zu folgen. IMHO der Punkt von Mustern ist es, Leuten eine leichtere Weise zu geben, zu verstehen, was Ihre APP tut, und in diesem Fall machte MVVM es viel schwieriger zu verstehen, als wenn ich es nicht benutzt hatte, und jetzt ein Antipattern war.

Zu Ihrer Information, ich stieß auf das gleiche Problem und ich denke, ich habe eine Arbeit gefunden, die keine Globals oder Statik erfordert, obwohl es vielleicht nicht die beste Antwort ist. Ich lasse die Jungs das selbst entscheiden.

In meinem Fall kennt das ViewModel, das das anzuzeigende Fenster instanziiert (man kann es ViewModelMain nennen), ebenfalls das LoginFormViewModel (anhand der obigen Situation als Beispiel).

Also habe ich eine Eigenschaft auf dem LoginFormViewModel vom Typ ICommand erstellt (Lässt es CloseWindowCommand aufrufen). Bevor ich dann .ShowDialog () im Window anwähle, setze ich die CloseWindowCommand -Eigenschaft auf dem LoginFormViewModel auf die window.Close () -Methode des Fensters, das ich instanziiert habe. Dann muss ich innerhalb des LoginFormViewModels nur CloseWindowCommand.Execute () aufrufen, um das Fenster zu schließen.

Es ist ein bisschen wie ein Workaround / Hack, aber es funktioniert gut, ohne das MVVM-Muster wirklich zu brechen.

Fühlen Sie sich frei, diesen process zu kritisieren, so viel Sie mögen, ich kann es nehmen! 🙂

Dies ist wahrscheinlich sehr spät, aber ich stieß auf das gleiche Problem und ich fand eine Lösung, die für mich funktioniert.

Ich kann nicht herausfinden, wie man eine App ohne Dialoge erstellt (vielleicht ist es nur ein Gedankenblock). Ich war also in einer Sackgasse mit MVVM und zeigte einen Dialog. Also bin ich auf diesen CodeProject-Artikel gestoßen:

http://www.codeproject.com/KB/WPF/XAMLDialog.aspx

Welches ist ein UserControl, das grundsätzlich erlaubt, ein Fenster innerhalb der visuellen Struktur eines anderen Fensters zu sein (in xaml nicht erlaubt). Es stellt auch eine boolesche DependencyProperty mit dem Namen IsShowing zur Verfügung.

Sie können einen Stil wie normalerweise in einem resourcedictionary festlegen, der den Dialog immer dann anzeigt, wenn die Content-Eigenschaft des Steuerelements! = Null via triggers:

   

In der Ansicht, in der Sie den Dialog anzeigen möchten, haben Sie einfach folgendes:

  

Und in Ihrem ViewModel müssen Sie lediglich die Eigenschaft auf einen Wert setzen (Hinweis: Die ViewModel-class muss INotifyPropertyChanged unterstützen, damit die Ansicht weiß, dass etwas passiert ist).

so:

 DialogViewModel = new DisplayViewModel(); 

Um das ViewModel mit der View zu vergleichen, sollten Sie Folgendes in einem resourcedictionary haben:

    

Mit all dem bekommst du einen einzeiligen Code um den Dialog zu zeigen. Das Problem, das Sie bekommen, ist, dass Sie den Dialog nicht wirklich mit dem obigen Code schließen können. Aus diesem Grund müssen Sie ein Ereignis in eine ViewModel-Basisklasse einfügen, von der DisplayViewModel erbt, und anstelle des obigen Codes schreiben Sie dies

  var vm = new DisplayViewModel(); vm.RequestClose += new RequestCloseHandler(DisplayViewModel_RequestClose); DialogViewModel = vm; 

Dann können Sie das Ergebnis des Dialogs über den Callback bearbeiten.

Das mag etwas komplex erscheinen, aber sobald die Grundlagen gelegt sind, ist es ziemlich einfach. Auch dies ist meine Implementierung, ich bin mir sicher, es gibt andere 🙂

Hoffe, das hilft, es hat mich gerettet.

Ok, also diese Frage ist fast 6 Jahre alt und ich kann immer noch nicht finden, was ich denke, es ist die richtige Antwort, also erlauben Sie mir, meine “2 Cent” zu teilen …

Ich habe tatsächlich 2 Möglichkeiten, es zu tun, das erste ist das einfache … das zweite auf der rechten, also wenn du nach dem richtigen suchst, springe einfach # 1 und springe zu # 2 :

1. Schnell und einfach (aber nicht vollständig)

Wenn ich nur ein kleines Projekt habe, erzeuge ich manchmal einfach eine CloseWindowAction im ViewModel:

  public Action CloseWindow { get; set; } // In MyViewModel.cs 

Und wer auch immer die View oder den View-Code hinterher kettet, ich habe einfach die Methode festgelegt, die die Action aufrufen wird:

(Denken Sie daran, MVVM ist über die Trennung der Ansicht und des ViewModel … der Code der View ist immer noch die Ansicht und solange es eine richtige Trennung gibt, verletzen Sie nicht das Muster)

Wenn ein ViewModel ein neues Fenster erstellt:

 private void CreateNewView() { MyView window = new MyView(); window.DataContext = new MyViewModel { CloseWindow = window.Close, }; window.ShowDialog(); } 

Oder wenn du es in deinem Hauptfenster haben willst, lege es einfach unter den Konstruktor deiner Sicht:

 public MyView() { InitializeComponent(); this.DataContext = new MainViewModel { CloseWindow = this.Close }; } 

Wenn Sie das Fenster schließen möchten, rufen Sie einfach die Aktion in Ihrem ViewModel auf.


2. Der richtige Weg

Jetzt ist der richtige Weg, um es zu tun, Prism (IMHO) zu verwenden, und alles darüber kann hier gefunden werden .

Sie können eine Interaktionsanfrage erstellen , sie mit den Daten füllen , die Sie in Ihrem neuen Fenster benötigen, sie zu Mittag essen, sie schließen und sogar Daten zurück erhalten . All dies gekapselt und MVVM genehmigt. Sie erhalten sogar einen Status, wie das Fenster geschlossen wurde , wie wenn der Benutzer Canceled oder Accepted (OK-Taste) das Fenster und Daten zurück, wenn Sie es brauchen . Es ist ein bisschen komplizierter und Antwort # 1, aber es ist viel vollständiger und ein empfohlenes Muster von Microsoft.

Der Link, den ich gegeben habe, enthält alle Codeschnipsel und Beispiele, also werde ich keinen Code hier hineinlegen, lese einfach den Artikel zum Download des Prism Quick Start und führe ihn aus, es ist wirklich einfach, nur ein wenig ausführlicher zu sein es funktioniert, aber die Vorteile sind größer als nur ein Fenster zu schließen.

Sie könnten das ViewModel ein Ereignis anzeigen lassen, für das sich die View anmeldet. Wenn das ViewModel dann entscheidet, wann die Ansicht geschlossen werden soll, wird das Ereignis ausgetriggers, das zum Schließen der Ansicht führt. Wenn Sie möchten, dass ein bestimmter Ergebniswert zurückgegeben wird, hätten Sie dafür eine Eigenschaft im ViewModel.

 public partial class MyWindow: Window { public ApplicationSelection() { InitializeComponent(); MyViewModel viewModel = new MyViewModel(); DataContext = viewModel; viewModel.RequestClose += () => { Close(); }; } } public class MyViewModel { //...Your code... public event Action RequestClose; public virtual void Close() { if (RequestClose != null) { RequestClose(); } } public void SomeFunction() { //...Do something... Close(); } } 

Warum nicht einfach das Fenster als Befehlsparameter übergeben?

C #:

  private void Cancel( Window window ) { window.Close(); } private ICommand _cancelCommand; public ICommand CancelCommand { get { return _cancelCommand ?? ( _cancelCommand = new Command.RelayCommand( ( window ) => Cancel( window ), ( window ) => ( true ) ) ); } } 

XAML:

  

Um die große Anzahl von Antworten hinzuzufügen, möchte ich Folgendes hinzufügen. Angenommen, Sie haben einen ICommand in Ihrem ViewModel, und Sie möchten, dass dieser Befehl sein Fenster schließt (oder eine andere Aktion für diese Angelegenheit), können Sie etwas wie folgt verwenden.

 var windows = Application.Current.Windows; for (var i=0;i< windows.Count;i++ ) if (windows[i].DataContext == this) windows[i].Close(); 

Es ist nicht perfekt und könnte schwierig zu testen sein (da es schwierig ist, statische Daten nachzuahmen), aber es ist sauberer (IMHO) als die anderen Lösungen.

Erick

Ich implementierte die Lösung von Joe White, stieß aber auf Probleme mit gelegentlichen ” DialogResult kann nur eingestellt werden, nachdem Window erstellt und als Dialog-Fehler angezeigt wurde .

Ich habe das ViewModel beibehalten, nachdem die Ansicht geschlossen wurde, und gelegentlich habe ich später eine neue Ansicht mit derselben VM geöffnet. Es scheint, dass beim Schließen der neuen Ansicht vor dem Speichern der alten Ansicht in DialogResultChanged versucht wurde, die DialogResult- Eigenschaft im geschlossenen Fenster festzulegen , wodurch der Fehler verursacht wird.

Meine Lösung war, DialogResultChanged zu ändern, um die IsLoaded- Eigenschaft des Fensters zu überprüfen :

 private static void DialogResultChanged( DependencyObject d, DependencyPropertyChangedEventArgs e) { var window = d as Window; if (window != null && window.IsLoaded) window.DialogResult = e.NewValue as bool?; } 

Nach dieser Änderung werden Anhänge zu geschlossenen Dialogen ignoriert.

Eine andere Lösung besteht darin, eine Eigenschaft mit INotifyPropertyChanged in View Model wie DialogResult zu erstellen und dann in Code Behind dies zu schreiben:

 public class SomeWindow: ChildWindow { private SomeViewModel _someViewModel; public SomeWindow() { InitializeComponent(); this.Loaded += SomeWindow_Loaded; this.Closed += SomeWindow_Closed; } void SomeWindow_Loaded(object sender, RoutedEventArgs e) { _someViewModel = this.DataContext as SomeViewModel; _someViewModel.PropertyChanged += _someViewModel_PropertyChanged; } void SomeWindow_Closed(object sender, System.EventArgs e) { _someViewModel.PropertyChanged -= _someViewModel_PropertyChanged; this.Loaded -= SomeWindow_Loaded; this.Closed -= SomeWindow_Closed; } void _someViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == SomeViewModel.DialogResultPropertyName) { this.DialogResult = _someViewModel.DialogResult; } } } 

Das wichtigste Fragment ist _someViewModel_PropertyChanged . DialogResultPropertyName kann eine öffentliche Zeichenfolge in SomeViewModel .

Ich benutze diese Art von Trick, um einige Änderungen in View Controls vorzunehmen, wenn dies in ViewModel schwierig ist. OnPropertyChanged in ViewModel können Sie alles, was Sie in View möchten, tun. ViewModel ist immer noch “Unit Testable” und einige kleine Codezeilen im Code machen keinen Unterschied.

Am Ende mischte ich Joe Whites Antwort und etwas Code von Adam Mills ‘Antwort , da ich ein Benutzersteuerelement in einem programmatisch erstellten Fenster anzeigen musste. Der DialogCloser muss also nicht auf dem Fenster sein, es kann auf dem Benutzer Steuerelement selbst sein

  

Und der DialogCloser wird das Fenster des Benutzersteuerelements finden, wenn es nicht an das Fenster selbst angehängt wurde.

 namespace Wpf { public static class DialogCloser { public static readonly DependencyProperty DialogResultProperty = DependencyProperty.RegisterAttached( "DialogResult", typeof(bool?), typeof(DialogCloser), new PropertyMetadata(DialogResultChanged)); private static void DialogResultChanged( DependencyObject d, DependencyPropertyChangedEventArgs e) { var window = d.GetWindow(); if (window != null) window.DialogResult = e.NewValue as bool?; } public static void SetDialogResult(DependencyObject target, bool? value) { target.SetValue(DialogResultProperty, value); } } public static class Extensions { public static Window GetWindow(this DependencyObject sender_) { Window window = sender_ as Window; return window ?? Window.GetWindow( sender_ ); } } } 

Ich würde diesen Weg gehen:

 using GalaSoft.MvvmLight; using GalaSoft.MvvmLight.Command; using GalaSoft.MvvmLight.Messaging; // View public partial class TestCloseWindow : Window { public TestCloseWindow() { InitializeComponent(); Messenger.Default.Register(this, (msg) => Close()); } } // View Model public class MainViewModel: ViewModelBase { ICommand _closeChildWindowCommand; public ICommand CloseChildWindowCommand { get { return _closeChildWindowCommand?? (_closeChildWindowCommand = new RelayCommand(() => { Messenger.Default.Send(new CloseWindowMsg()); })); } } } public class CloseWindowMsg { } 

Ich habe alle Antworten gelesen, aber ich muss sagen, die meisten sind einfach nicht gut genug oder noch schlimmer.

Sie können dies mit der DialogService- class, die dafür zuständig ist, das Dialogfenster anzuzeigen und das Ergebnis des Dialoges zurückzugeben, gut handhaben. Ich habe ein Beispielprojekt erstellt , das seine Implementierung und Verwendung demonstriert.

Hier sind die wichtigsten Teile:

 //we will call this interface in our viewmodels public interface IDialogService { bool? ShowDialog(object dialogViewModel, string caption); } //we need to display logindialog from mainwindow public class MainWindowViewModel : ViewModelBase { public string Message {get; set;} public void ShowLoginCommandExecute() { var loginViewModel = new LoginViewModel(); var dialogResult = this.DialogService.ShowDialog(loginViewModel, "Please, log in"); //after dialog is closed, do someting if (dialogResult == true && loginViewModel.IsLoginSuccessful) { this.Message = string.Format("Hello, {0}!", loginViewModel.Username); } } } public class DialogService : IDialogService { public bool? ShowDialog(object dialogViewModel, string caption) { var contentView = ViewLocator.GetView(dialogViewModel); var dlg = new DialogWindow { Title = caption }; dlg.PART_ContentControl.Content = contentView; return dlg.ShowDialog(); } } 

Ist das nicht einfach einfacher? geradliniger, lesbarer und last but not least leichter zu debuggen als EventAggregator oder andere ähnliche Lösungen?

Wie Sie sehen können, habe ich in meinen View-Modellen den ersten Ansatz von ViewModel verwendet, der in meinem Post hier beschrieben ist: Bewährtes Verfahren zum Aufruf von View von ViewModel in WPF

Natürlich muss der DialogService.ShowDialog der DialogService.ShowDialog mehr Optionen haben, um den Dialog zu konfigurieren, zB Schaltflächen und Befehle, die er ausführen soll. Es gibt verschiedene Möglichkeiten, dies zu tun, aber es ist außerhalb des Geltungsbereichs 🙂

Während dies nicht die Frage beantwortet, wie dies über das Viewmodel zu tun ist, zeigt dies, wie dies unter Verwendung von nur XAML + dem Blend SDK durchgeführt wird.

Ich entschied mich, zwei Dateien aus dem Blend-SDK herunterzuladen und zu verwenden, die Sie beide als Paket von Microsoft über NuGet herunterladen können. Die Dateien sind:

System.Windows.Interactivity.dll und Microsoft.Expression.Interactions.dll

Microsoft.Expression.Interactions.dll gibt Ihnen nette Fähigkeiten wie die Fähigkeit, Eigenschaft zu setzen oder eine Methode auf Ihrem Viewmodel oder anderem Ziel anzurufen und hat andere Widgets innerhalb auch.

Einige XAML:

        

Beachten Sie, dass Sie mit den Eigenschaften IsDefault und IsCancel fortfahren können, solange das Fenster mit Window.ShowDialog () angezeigt wird.
Ich persönlich hatte Probleme mit einer Schaltfläche, bei der die IsDefault-Eigenschaft auf “true” gesetzt war, aber beim Laden der Seite ausgeblendet wurde. Es schien nicht gut spielen zu wollen, nachdem es gezeigt wurde, also setze ich einfach die Window.DialogResult Eigenschaft, wie oben gezeigt, und es funktioniert für mich.

Hier ist die einfache errorsfreie Lösung (mit Quellcode), es funktioniert für mich.

  1. Derive your ViewModel from INotifyPropertyChanged

  2. Create a observable property CloseDialog in ViewModel

     public void Execute() { // Do your task here // if task successful, assign true to CloseDialog CloseDialog = true; } private bool _closeDialog; public bool CloseDialog { get { return _closeDialog; } set { _closeDialog = value; OnPropertyChanged(); } } public event PropertyChangedEventHandler PropertyChanged; private void OnPropertyChanged([CallerMemberName]string property = "") { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(property)); } } 

    }

  3. Attach a Handler in View for this property change

      _loginDialogViewModel = new LoginDialogViewModel(); loginPanel.DataContext = _loginDialogViewModel; _loginDialogViewModel.PropertyChanged += OnPropertyChanged; 
  4. Now you are almost done. In the event handler make DialogResult = true

     protected void OnPropertyChanged(object sender, PropertyChangedEventArgs args) { if (args.PropertyName == "CloseDialog") { DialogResult = true; } } 

Create a Dependency Property in your View /any UserControl (or Window you want to close). Like below:

  public bool CloseTrigger { get { return (bool)GetValue(CloseTriggerProperty); } set { SetValue(CloseTriggerProperty, value); } } public static readonly DependencyProperty CloseTriggerProperty = DependencyProperty.Register("CloseTrigger", typeof(bool), typeof(ControlEventBase), new PropertyMetadata(new PropertyChangedCallback(OnCloseTriggerChanged))); private static void OnCloseTriggerChanged(DependencyObject dp, DependencyPropertyChangedEventArgs e) { //write Window Exit Code } 

And bind it from your ViewModel’s property :

  

Property In VeiwModel :

 private bool closeWindow; public bool CloseWindow { get { return closeWindow; } set { closeWindow = value; RaiseChane("CloseWindow"); } } 

Now trigger the close operation by changing the CloseWindow value in ViewModel. 🙂

Behavior is the most convenient way here.

  • From one hand, it can be binded to the given viewmodel (that can signal “close the form!”)

  • From another hand, it has access to the form itself so can subscribe to necessary form-specific events, or show confirmation dialog, or anything else.

Writing necessary behavior can be seen boring very first time. However, from now on, you can reuse it on every single form you need by exact one-liner XAML snippet. And if necessary, you can extract it as a separate assembly so it can be included into any next project you want.

Where you need to close the window, simply put this in the viewmodel:

ta-da

  foreach (Window window in Application.Current.Windows) { if (window.DataContext == this) { window.Close(); return; } } 
 Application.Current.MainWindow.Close() 

Thats enough!