Manejo de cuadros de diálogo en WPF con MVVM

En el patrón MVVM para WPF, el manejo de diálogos es una de las operaciones más complejas. Como su modelo de vista no sabe nada sobre la vista, la comunicación de diálogo puede ser interesante. Puedo exponer un ICommand que cuando la vista lo invoca, puede aparecer un diálogo.

¿Alguien sabe de una buena manera de manejar los resultados de los diálogos? Estoy hablando de diálogos de Windows como MessageBox.

Una de las formas en que lo hicimos fue tener un evento en el modelo de vista al que la vista se suscribiría cuando se requiriera un diálogo.

public event EventHandler RequiresDeleteDialog; 

Esto está bien, pero significa que la vista requiere código que es algo de lo que me gustaría mantenerme alejado.

Sugiero renunciar a los diálogos modales de los años 90 y en su lugar implementar un control como una superposición (canvas + posicionamiento absoluto) con visibilidad vinculada a un booleano en la VM. Más cerca de un control tipo ajax.

Esto es muy útil:

  

como en:

  

Así es como he implementado uno como control de usuario. Al hacer clic en la ‘x’ se cierra el control en una línea de código en el código de control de usuario detrás. (Debido a que tengo mis Vistas en un .exe y ViewModels en un dll, no me siento mal por el código que manipula la IU).

Diálogo Wpf

Deberías usar un mediador para esto. Mediator es un patrón de diseño común también conocido como Messenger en algunas de sus implementaciones. Es un paradigma de tipo Registrar / Notificar y permite que su ViewModel y Vistas se comuniquen a través de un mecanismo de mensajería de bajo acoplamiento.

Debería consultar el grupo de Google WPF Disciples y simplemente buscar Mediator. Estarás muy feliz con las respuestas …

Sin embargo, puedes comenzar con esto:

http://joshsmithonwpf.wordpress.com/2009/04/06/a-mediator-prototype-for-wpf-apps/

¡Disfruta!

Editar: puede ver la respuesta a este problema con el MVVM Light Toolkit aquí:

http://mvvmlight.codeplex.com/Thread/View.aspx?ThreadId=209338

Un buen diálogo de MVVM debería:

  1. Declararse solo con XAML.
  2. Obtiene todo su comportamiento de enlace de datos.

Lamentablemente, WPF no proporciona estas características. Mostrar un cuadro de diálogo requiere una llamada de código subyacente a ShowDialog (). La clase Window, que admite diálogos, no se puede declarar en XAML por lo que no se puede unir fácilmente al DataContext.

Para resolver esto, escribí un control de trozos XAML que se encuentra en el árbol lógico y retransmite enlaces de datos a una ventana y maneja mostrando y ocultando el diálogo. Puede encontrarlo aquí: http://www.codeproject.com/KB/WPF/XAMLDialog.aspx

Es realmente simple de usar y no requiere cambios extraños en su ViewModel y no requiere eventos o mensajes. La llamada básica se ve así:

  

Probablemente desee agregar un estilo que establezca Mostrar. Lo explico en mi artículo. Espero que esto te ayude.

Uso este enfoque para diálogos con MVVM.

Todo lo que tengo que hacer ahora es llamar lo siguiente desde mi modelo de vista.

 var result = this.uiDialogService.ShowDialog("Dialogwindow title goes here", dialogwindowVM); 

Mi solución actual resuelve la mayoría de los problemas que mencionó, pero está completamente abstraída de los elementos específicos de la plataforma y puede reutilizarse. Además, no utilicé ningún código subyacente solo vinculante con DelegateCommands que implementan ICommand. El diálogo es básicamente una vista, un control separado que tiene su propio modelo de vista y se muestra desde el modelo de vista de la pantalla principal, pero se desencadena desde la interfaz de usuario a través del enlace DelagateCommand.

Vea la solución completa de Silverlight 4 aquí Diálogos modales con MVVM y Silverlight 4

Realmente tuve problemas con este concepto durante un tiempo cuando aprendí (todavía estoy aprendiendo) MVVM. Lo que decidí, y lo que creo que otros ya decidieron pero que no me quedó claro es este:

Mi idea original era que un ViewModel no debería poder llamar a un cuadro de diálogo directamente, ya que no tiene nada que ver con la decisión de cómo debería aparecer un diálogo. Debido a esto, comencé a pensar en cómo podía transmitir mensajes de forma muy similar a como lo haría en MVP (es decir, View.ShowSaveFileDialog ()). Sin embargo, creo que este es el enfoque equivocado.

Está bien que ViewModel llame directamente a un cuadro de diálogo. Sin embargo, cuando está probando un ViewModel, eso significa que el cuadro de diálogo emergerá durante la prueba o fallará todo (nunca lo intenté).

Entonces, lo que debe suceder es que mientras se prueba se use una versión de “prueba” de su diálogo. Esto significa que para el diálogo que tiene, necesita crear una interfaz y simular la respuesta de diálogo o crear una prueba simulada que tendrá un comportamiento predeterminado.

Ya debería estar utilizando algún tipo de Localizador de servicios o IoC que pueda configurar para proporcionarle la versión correcta según el contexto.

Al usar este enfoque, su ViewModel aún se puede probar y, dependiendo de cómo se burlan de sus diálogos, puede controlar el comportamiento.

Espero que esto ayude.

Use un comando freezable

       
 public class MessageBoxCommand : Freezable, ICommand { public static readonly DependencyProperty YesCommandProperty = DependencyProperty.Register( "YesCommand", typeof (ICommand), typeof (MessageBoxCommand), new FrameworkPropertyMetadata(null) ); public static readonly DependencyProperty OKCommandProperty = DependencyProperty.Register( "OKCommand", typeof (ICommand), typeof (MessageBoxCommand), new FrameworkPropertyMetadata(null) ); public static readonly DependencyProperty CancelCommandProperty = DependencyProperty.Register( "CancelCommand", typeof (ICommand), typeof (MessageBoxCommand), new FrameworkPropertyMetadata(null) ); public static readonly DependencyProperty NoCommandProperty = DependencyProperty.Register( "NoCommand", typeof (ICommand), typeof (MessageBoxCommand), new FrameworkPropertyMetadata(null) ); public static readonly DependencyProperty MessageProperty = DependencyProperty.Register( "Message", typeof (string), typeof (MessageBoxCommand), new FrameworkPropertyMetadata("") ); public static readonly DependencyProperty MessageBoxButtonsProperty = DependencyProperty.Register( "MessageBoxButtons", typeof(MessageBoxButton), typeof(MessageBoxCommand), new FrameworkPropertyMetadata(MessageBoxButton.OKCancel) ); public ICommand YesCommand { get { return (ICommand) GetValue(YesCommandProperty); } set { SetValue(YesCommandProperty, value); } } public ICommand OKCommand { get { return (ICommand) GetValue(OKCommandProperty); } set { SetValue(OKCommandProperty, value); } } public ICommand CancelCommand { get { return (ICommand) GetValue(CancelCommandProperty); } set { SetValue(CancelCommandProperty, value); } } public ICommand NoCommand { get { return (ICommand) GetValue(NoCommandProperty); } set { SetValue(NoCommandProperty, value); } } public MessageBoxButton MessageBoxButtons { get { return (MessageBoxButton)GetValue(MessageBoxButtonsProperty); } set { SetValue(MessageBoxButtonsProperty, value); } } public string Message { get { return (string) GetValue(MessageProperty); } set { SetValue(MessageProperty, value); } } public void Execute(object parameter) { var messageBoxResult = MessageBox.Show(Message); switch (messageBoxResult) { case MessageBoxResult.OK: OKCommand.Execute(null); break; case MessageBoxResult.Yes: YesCommand.Execute(null); break; case MessageBoxResult.No: NoCommand.Execute(null); break; case MessageBoxResult.Cancel: if (CancelCommand != null) CancelCommand.Execute(null); //Cancel usually means do nothing ,so can be null break; } } public bool CanExecute(object parameter) { return true; } public event EventHandler CanExecuteChanged; protected override Freezable CreateInstanceCore() { throw new NotImplementedException(); } } 

Hay dos buenas maneras de hacerlo, 1) un servicio de diálogo (fácil, limpio) y 2) ver asistido. La vista asistida proporciona algunas características interesantes, pero generalmente no vale la pena.

SERVICIO DE DIÁLOGO

a) una interfaz de servicio de diálogo como vía constructor o algún contenedor de dependencia:

interface IDialogService { Task ShowDialogAsync(DialogViewModel dlgVm); }

b) Su implementación de IDialogService debe abrir una ventana (o inyectar algún control en la ventana activa), crear una vista correspondiente al nombre del tipo de dlgVm dado (usar registro o convención de contenedor o un ContentPresenter con tipo DataTemplates asociado). ShowDialogAsync debería crear un TaskCompletionSource y devolver su .Task Proptery. La clase DialogViewModel en sí necesita un evento que pueda invocar en la clase derivada cuando desee cerrar, y observe en la vista de diálogo para cerrar / ocultar realmente el diálogo y completar TaskCompletionSource.

b) Para usar, simplemente llame a await this.DialogService.ShowDialog (myDlgVm) en su instancia de alguna clase DialogViewModel-derived. Después de esperar devoluciones, mira las propiedades que has agregado en tu máquina virtual de diálogo para determinar qué sucedió; ni siquiera necesitas una callback.

VER ASISTIDO

Esto tiene su vista escuchando un evento en el modelo de vista. Todo esto podría incluirse en un Comportamiento de combinación para evitar el código subyacente y el uso de recursos si así lo desea (FMI, subclase de la clase “Comportamiento” para ver un tipo de propiedad adjunta Blendable con esteroides). Por ahora, haremos esto manualmente en cada vista:

a) Cree un OpenXXXXXDialogEvent con una carga útil personalizada (una clase derivada DialogViewModel).

b) Haga que la vista se suscriba al evento en su evento OnDataContextChanged. Asegúrese de ocultar y anular la suscripción si el antiguo valor! = Null y en el evento descargado de la ventana.

c) Cuando se dispara el evento, haga que la vista abra su vista, que podría estar en un recurso en su página, o podría ubicarla por convención en cualquier otro lugar (como en el enfoque del servicio de diálogo).

Este enfoque es más flexible, pero requiere más trabajo para usar. No lo uso mucho. La única buena ventaja es la capacidad de colocar la vista físicamente dentro de una pestaña, por ejemplo. He utilizado un algoritmo para colocarlo en los límites del control de usuario actual, o si no es lo suficientemente grande, recorrer el árbol visual hasta que se encuentre un contenedor lo suficientemente grande.

Esto permite que los diálogos estén cerca del lugar en el que se usan, solo atenúa la parte de la aplicación relacionada con la actividad actual y permite que el usuario se mueva dentro de la aplicación sin tener que alejar manualmente los diálogos, incluso tener múltiples cuasi- los diálogos modales se abren en diferentes tabs o sub-vistas.

Creo que el manejo de un diálogo debe ser responsabilidad de la vista, y la vista debe tener un código que lo soporte.

Si cambia el ViewModel – Ver la interacción para manejar diálogos, ViewModel depende de esa implementación. La forma más sencilla de tratar este problema es hacer que View sea responsable de realizar la tarea. Si eso significa mostrar un cuadro de diálogo, entonces está bien, pero también podría ser un mensaje de estado en la barra de estado, etc.

Mi punto es que el objective del patrón MVVM es separar la lógica empresarial de la GUI, por lo que no debe mezclar la lógica GUI (para mostrar un diálogo) en la capa empresarial (ViewModel).

Una alternativa interesante es usar controladores que son responsables de mostrar las vistas (diálogos).

Cómo funciona esto se muestra en el Marco de Aplicación de WPF (WAF) .

¿Por qué no simplemente plantear un evento en la máquina virtual y suscribirse al evento en la vista? Esto mantendría separada la lógica de la aplicación y la vista, y aún le permitirá usar una ventana secundaria para los cuadros de diálogo.

Implementé un comportamiento que escucha un mensaje de ViewModel. Está basado en la solución Laurent Bugnion, pero como no usa código detrás y es más reutilizable, creo que es más elegante.

Cómo hacer que WPF se comporte como si MVVM fuera compatible de fábrica

Creo que la vista podría tener código para manejar el evento desde el modelo de vista.

Dependiendo del evento / escenario, también podría tener un activador de evento que se suscriba para ver eventos del modelo, y una o más acciones para invocar en respuesta.

Tuve la misma situación y envolví el MessageBox en un control invisible de diseñador. Los detalles están en mi blog

http://geekswithblogs.net/mukapu/archive/2010/03/12/user-prompts-messagebox-with-mvvm.aspx

Lo mismo puede extenderse a cualquier cuadro de diálogo modal, control de búsqueda de archivos, etc.

Hice rodar mi propio cargador de ventanas descrito en una respuesta a esta pregunta:

Administrar múltiples vistas de WPF en una aplicación

Karl Shifflett ha creado una aplicación de muestra para mostrar cuadros de diálogo utilizando el enfoque de servicio y el enfoque Prism InteractionRequest.

Me gusta el enfoque de servicio: es menos flexible, por lo que es menos probable que los usuarios rompan algo 🙂 También es consistente con la parte de WinForms de mi aplicación (MessageBox.Show) Pero si planea mostrar muchos diálogos diferentes, InteractionRequest es una mejor manera de ir.

http://karlshifflett.wordpress.com/2010/11/07/in-the-box-ndash-mvvm-training/

Sé que es una vieja pregunta, pero cuando hice esta búsqueda, encontré muchas preguntas relacionadas, pero no encontré una respuesta realmente clara. ¡Así que hago mi propia implementación de un dialogbox / messagebox / popin, y lo comparto!
Creo que es “prueba de MVVM”, y trato de hacerlo simple y correcto, pero soy nuevo en WPF, así que no dude en comentar, o incluso hacer una solicitud de extracción.

https://github.com/Plasma-Paris/Plasma.WpfUtils

Puedes usarlo así:

 public RelayCommand YesNoMessageBoxCommand { get; private set; } async void YesNoMessageBox() {    var result = await _Service.ShowMessage("This is the content of the message box", "This is the title", System.Windows.MessageBoxButton.YesNo);    if (result == System.Windows.MessageBoxResult.Yes)        // [...] } 

O así si quieres popin más sofisticado:

 var result = await _Service.ShowCustomMessageBox(new MyMessageBoxViewModel { /* What you want */ }); 

Y está mostrando cosas como esta:

2

Después de pasar algo de tiempo con él, finalmente se me ocurrió la siguiente solución. Algunas ventajas clave de este enfoque son:

  1. Implementa el IDialogService de MVVM Light.
  2. La vista no necesita agregar la referencia de MVVM Light.
  3. VM no necesita hacer ninguna actividad de nivel de presentación. Ni siquiera necesita una referencia de PresentationFramework .
  4. Utiliza el propio canal Messenger de MVVM Light, por lo que la presentación y las capas VM están desacopladas.
  5. Admite diálogos con un valor de retorno, como preguntas Sí / No o situaciones de Aceptar / Cancelar.
  6. Admite diálogos personalizados.

Aquí está la implementación de IDialogService (entra en el proyecto ViewModel ):

 using System; using System.Linq; using System.Threading.Tasks; namespace VM { public enum MessageBoxButtonVM { OK, OKCancel, YesNo } public enum MessageBoxImageVM { None, Information, Question, Error } public class MessageBoxArgs { public MessageBoxButtonVM Buttons { get; set; } public MessageBoxImageVM Icon { get; set; } public string Title { get; set; } public string Message { get; set; } } //For custom dialogs that return a value public class MessageBoxNotificationWithAction { private readonly Action _callback; public MessageBoxArgs Notification { get; set; } public MessageBoxNotificationWithAction(MessageBoxArgs notification, Action callback) { Notification = notification; CheckCallback(callback); _callback = callback; } public virtual void Execute(T argument) { _callback.Invoke(argument); } private static void CheckCallback(Delegate callback) { if (callback == null) { throw new ArgumentNullException(nameof(callback), "Callback must not be null"); } } } ///  /// Provides an implementation-agnostic way of communicating with the user through dialog boxes. Clients must register for communication messages using /// MVVM Light messaging system. ///  public class DialogService : GalaSoft.MvvmLight.Views.IDialogService { private static GalaSoft.MvvmLight.Messaging.IMessenger Messenger = GalaSoft.MvvmLight.Messaging.Messenger.Default; private string _ProductName = ""; public string ProductName { get { if (_ProductName == "") { //The following statement returns the Title attribute of the current assembly, as defined in project properties (Assembly Information dialog). var TitleAttrib = System.Reflection.Assembly.GetExecutingAssembly().GetCustomAttributesData().First(x => x.AttributeType.Name == "AssemblyTitleAttribute"); if (TitleAttrib != null) { _ProductName = TitleAttrib.ConstructorArguments[0].Value.ToString(); } else { _ProductName = "Default Application Name"; } } return _ProductName; } } public Task ShowError(Exception error, string title, string buttonText, Action afterHideCallback) { return ShowError(error.Message, title, buttonText, afterHideCallback); } public Task ShowMessage(string message, string title) { return Task.Run(() => MessengerSend(message, title, MessageBoxButtonVM.OK, MessageBoxImageVM.Error)); } public Task ShowError(string message, string title, string buttonText, Action afterHideCallback) { return Task.Run(() => { MessengerSend(message, title, MessageBoxButtonVM.OK, MessageBoxImageVM.Error); afterHideCallback?.Invoke(); }); } public Task ShowMessage(string message, string title, string buttonText, Action afterHideCallback) { return Task.Run(() => { MessengerSend(message, title); afterHideCallback?.Invoke(); }); } public Task ShowMessage(string message, string title, string buttonConfirmText, string buttonCancelText, Action afterHideCallback) { if ((buttonConfirmText == "OK" && buttonCancelText == "Cancel") || (buttonConfirmText == "Yes" && buttonCancelText == "No")) { return Task.Run(() => { MessageBoxButtonVM btn; if (buttonConfirmText == "OK") btn = MessageBoxButtonVM.OKCancel; else btn = MessageBoxButtonVM.YesNo; bool Response = false; Messenger.Send(new MessageBoxNotificationWithAction( new MessageBoxArgs() { Buttons = btn, Icon = MessageBoxImageVM.Question, Title = (string.IsNullOrEmpty(title) ? _ProductName : title), Message = message }, (result) => Response = result )); afterHideCallback?.Invoke(Response); return Response; }); } else throw new ArgumentException($"{nameof(buttonConfirmText)} and {nameof(buttonCancelText)} must either be OK/Cancel or Yes/No."); } ///  /// For debugging purpose only ///  ///  ///  ///  public Task ShowMessageBox(string message, string title) => ShowMessage(message, title); private void MessengerSend(string msg, string title = "", MessageBoxButtonVM btn = MessageBoxButtonVM.OK, MessageBoxImageVM icon = MessageBoxImageVM.Information) { Messenger.Send(new MessageBoxArgs() { Buttons = MessageBoxButtonVM.OK, Icon = MessageBoxImageVM.Information, Title = (string.IsNullOrEmpty(title) ? _ProductName : title), Message = msg }); } } } 

Aquí está la capa de presentación (entra en el proyecto Ver )

 using System.Windows; using VM; namespace View { class DialogPresenter { private Window _Parent; public DialogPresenter() { //For simple information boxes GalaSoft.MvvmLight.Messaging.Messenger.Default.Register(this, (arg) => ShowDialog(arg)); //For Yes/No or OK/Cancel dialog boxes. GalaSoft.MvvmLight.Messaging.Messenger.Default.Register>(this, (arg) => arg.Execute(ShowDialog(arg.Notification))); //For notifications that require a string response (such as Manual Timeslot Description) GalaSoft.MvvmLight.Messaging.Messenger.Default.Register>(this, (arg) => arg.Execute(ShowStringInputDialog(arg.Notification.Title, arg.Notification.Message))); } private bool ShowDialog(MessageBoxArgs arg) { MessageBoxButton btn = MessageBoxButton.OK; MessageBoxImage ico = MessageBoxImage.None; switch (arg.Buttons) { case MessageBoxButtonVM.OK: btn = MessageBoxButton.OK; break; case MessageBoxButtonVM.OKCancel: btn = MessageBoxButton.OKCancel; break; case MessageBoxButtonVM.YesNo: btn = MessageBoxButton.YesNo; break; } switch (arg.Icon) { case MessageBoxImageVM.Error: ico = MessageBoxImage.Error; break; case MessageBoxImageVM.Information: ico = MessageBoxImage.Information; break; case MessageBoxImageVM.None: ico = MessageBoxImage.None; break; case MessageBoxImageVM.Question: ico = MessageBoxImage.Question; break; } bool Result = false; _Parent.Dispatcher.Invoke(() => { var Res = MessageBox.Show(arg.Message, arg.Title, btn, ico); Result = (Res == MessageBoxResult.OK || Res == MessageBoxResult.Yes); }); return Result; } private string ShowStringInputDialog(string title, string description, string value = "", int maxLength = 100) { string Result = null; _Parent.Dispatcher.Invoke(() => { //InputBox is a WPF Window I created for taking simple //string values from the user. This also shows that you can //any custom dialog using this approach. InputBox input = new InputBox(); input.Title = title; input.Owner = _Parent; if (input.ShowDialog(description, value, maxLength).Value) Result=input.Value; else Result=null; }); return Result; } //Call this somewhere at application startup so that the dialog boxes //appear as child windows. public void SetParentWindow(Window parent) { _Parent = parent; } } } 

Estaba ponderando un problema similar al preguntar cómo debería ser el modelo de vista para una tarea o un diálogo .

Mi solución actual se ve así:

 public class SelectionTaskModel : ViewModel where TChoosable : ViewModel { public SelectionTaskModel(ICollection choices); public ReadOnlyCollection Choices { get; } public void Choose(TChoosable choosen); public void Abort(); } 

Cuando el modelo de vista decide que se requiere la entrada del usuario, extrae una instancia de SelectionTaskModel con las posibles opciones para el usuario. La infraestructura se encarga de mostrar la vista correspondiente, que en el momento adecuado llamará a la función Choose() con la elección del usuario.

Luché con el mismo problema. He encontrado una forma de intercomunicarse entre View y ViewModel. Puede iniciar el envío de un mensaje desde el ViewModel a la Vista para indicarle que muestre un mensaje y se lo informará con el resultado. Entonces ViewModel puede responder al resultado devuelto por la Vista.

Demuestro esto en mi blog :

Puedo ser de la opinión muy impopular de que confiar solo en XAML en su opinión es severamente limitante. Creo que podemos separar mejor View y ViewModel al utilizar el código subyacente de la vista para ayudarnos a implementar la visualización de mensajes o recostackción de datos específicos. Al permitir que ViewModel lleve a cabo su propia lógica y devuelva los resultados de tales, como propiedades o disparando eventos, podemos diseñar la vista para hacer casi cualquier cosa que queramos (diálogos incluidos).

En un caso específico, necesitaba un diálogo de Entrada de nombre que simplemente toma el nombre de una familia y establece una propiedad en ViewModel para usarlo más adelante. Debido a la cantidad de información que necesitaba recostackr, tener cuadros de diálogo para captar cosas como nombres simplificaba la experiencia del usuario lo suficiente como para que no pareciera “abrumadora”.

Sabiendo esto, utilicé un evento de clic en el botón específico para llamar a un nuevo diálogo personalizado que diseñé para este propósito. Cuando llamo al diálogo, paso el DataContext de la ventana padre como un objeto en el constructor del cuadro de diálogo. Luego configuro el DataContext del cuadro de diálogo a través de este parámetro.

  public NameEntryDialog(object dataContext) { InitializeComponent(); DataContext = dataContext; } 

Ahora que las dos vistas tenían un DataContext común, pude establecer cualquier propiedad en ViewModel vinculando los nodos de mi diálogo. Esto no requirió ningún tipo de soluciones o lagunas importantes para funcionar correctamente.

Del mismo modo, una vez que se produjo la verificación de datos en el modelo de vista (sin caracteres especiales o nombres de caracteres individuales, etc.) tuve el ViewModel establecido dos propiedades, 1) una propiedad validationSuccessful y 2) una propiedad ErrorMessage.

Once the dialog box was closed, my code-behind simply inspected those properties and called a MessageBox if validation was not successful (displaying the error message set in the property of my ViewModel). You could also accomplish this by subscribing to events in your ViewModel which, when fired, could call a Dialog or MessageBox in your view. I also had the logic of the ViewModel make the CanExecute property of the follow up “save” command “false” so that the user could not continue until those errors (and others during data collection) were corrected.

By using the code-behind I was able to completely separate the View from the ViewModel, and could also perform testing without the need for a view at all.

This also allows me to design new views which can interact with the same properties in the ViewModel independent of the need for a dialog. I could use a UserControl, a dialog, a single Window, etc. As long as I address all of the properties presented in my ViewModel, the sky is the limit. One thing that I’ve found to be true in almost every convoluted attempt at handling this problem was that the dialog box is eventually called SOMEWHERE (strong emphasis on somewhere) in code. Why not just skip all of that hassle and do it right inside your view?

I’ve written a fairly comprehensive article about this very topic and also developed a pop-in library for MVVM Dialogs. Strict adherence to MVVM is not only possible but very clean when implemented properly, and it can be easily extended to third-party libraries that don’t adhere to it themselves:

https://www.codeproject.com/Articles/820324/Implementing-Dialog-Boxes-in-MVVM

Sorry, but I have to chime in. I have been through several of the suggested solutions, before finding the Prism.Wpf.Interactivity namespace in the Prism project. You can use interaction requests and popup window action to either roll a custom window or for simpler needs there are built in Notification and Confirmation popups. These create true windows and are managed as such. you can pass a context object with any dependencies you need in the dialog. We use this solution at my work since I found it. We have numerous senior devs here and noone has come up with anything better. Our previous solution was the dialog service into an overlay and using a presenter class to make it happen, but you had to have factories for all of the dialog viewmodels, etc.

This isn’t trivial but it also isn’t super complicated. And it is built in to Prism and is therefore best (or better) practice IMHO.

My 2 cents!

EDIT: yes I agree this is not a correct MVVM approach and I am now using something similar to what is suggested by blindmeis.

One of the way you could to this is

In your Main View Model (where you open the modal):

 void OpenModal() { ModalWindowViewModel mwvm = new ModalWindowViewModel(); Window mw = new Window(); mw.content = mwvm; mw.ShowDialog() if(mw.DialogResult == true) { // Your Code, you can access property in mwvm if you need. } } 

And in your Modal Window View/ViewModel:

XAML:

   

ViewModel:

 public ICommand OkCommand { get { if (_okCommand == null) { _okCommand = new ActionCommand(DoOk, CanDoOk); } return _okCommand ; } } void DoOk(Window win) {  win.DialogResult = true; win.Close(); } bool CanDoOk(Window win) { return true; } 

or similar to what is posted here WPF MVVM: How to close a window