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 y 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<MyDeleteArgs> RequiresDeleteDialog;
Esto está bien, pero significa que la vista requiere un código que es algo de lo que me gustaría alejarme.
Respuestas:
Sugiero renunciar a los cuadros de diálogo modales de la década de 1990 y, en su lugar, implementar un control como una superposición (lienzo + posicionamiento absoluto) con visibilidad vinculada a un booleano en la VM. Más cerca de un control de tipo ajax.
Esto es muy útil:
como en:
Así es como tengo uno implementado 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 del control de usuario detrás. (Como tengo mis Vistas en un .exe y ViewModels en un dll, no me siento mal por el código que manipula la IU).
fuente
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 Registrarse / 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 Discípulos de Google WPF y simplemente buscar Mediador. Estarás muy contento con las respuestas ...
Sin embargo, puede comenzar con esto:
http://joshsmithonwpf.wordpress.com/2009/04/06/a-mediator-prototype-for-wpf-apps/
¡A disfrutar!
Editar: puede ver la respuesta a este problema con el MVVM Light Toolkit aquí:
http://mvvmlight.codeplex.com/Thread/View.aspx?ThreadId=209338
fuente
Un buen diálogo MVVM debería:
Desafortunadamente, WPF no proporciona estas características. Mostrar un 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 enlazar fácilmente a los datosDataContext
.Para resolver esto, escribí un control de código auxiliar XAML que se encuentra en el árbol lógico y retransmite el enlace de datos a
Window
y maneja mostrar y ocultar el diálogo. Puede encontrarlo aquí: http://www.codeproject.com/KB/WPF/XAMLDialog.aspxEs realmente simple de usar y no requiere ningún cambio extraño en su ViewModel y no requiere eventos o mensajes. La llamada básica se ve así:
Probablemente desee agregar un estilo que establezca
Showing
. Lo explico en mi artículo. Espero que esto te ayude.fuente
"Showing a dialog requires a code-behind"
mmm puedes llamarlo en ViewModelUtilizo este enfoque para los diálogos con MVVM.
Todo lo que tengo que hacer ahora es llamar a lo siguiente desde mi modelo de vista.
fuente
Mi solución actual resuelve la mayoría de los problemas que mencionó, pero está completamente abstraída de las cosas específicas de la plataforma y puede reutilizarse. También no utilicé ningún enlace de código subyacente solo con DelegateCommands que implementan ICommand. El diálogo es básicamente una vista: un control separado que tiene su propio ViewModel y se muestra desde el ViewModel de la pantalla principal, pero se activa 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
fuente
Realmente luché con este concepto por un tiempo cuando aprendí (todavía estoy aprendiendo) MVVM. Lo que decidí, y lo que creo que otros ya decidieron pero que no estaba claro para mí es esto:
Mi pensamiento original era que un ViewModel no debería poder llamar a un cuadro de diálogo directamente, ya que no tiene nada que decidir cómo debería aparecer un diálogo. Debido a esto, comencé a pensar en cómo podría pasar mensajes como lo haría en MVP (es decir, View.ShowSaveFileDialog ()). Sin embargo, creo que este es el enfoque equivocado.
Está bien que un ViewModel llame a un diálogo directamente. Sin embargo, cuando está probando un ViewModel, eso significa que el cuadro de diálogo aparecerá durante la prueba o fallará por completo (nunca lo intentó).
Entonces, lo que debe suceder es que mientras se realiza la prueba, se debe usar una versión de "prueba" de su diálogo. Esto significa que para cada diálogo que tenga, debe crear una interfaz y simular la respuesta del diálogo o crear una simulación de prueba 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.
Con este enfoque, su ViewModel aún es comprobable y, dependiendo de cómo se burle de sus cuadros de diálogo, puede controlar el comportamiento.
Espero que esto ayude.
fuente
Hay dos buenas maneras de hacer esto: 1) un servicio de diálogo (fácil, limpio) y 2) vista asistida. Ver asistido 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 debería abrir una ventana (o inyectar algún control en la ventana activa), crear una vista correspondiente al nombre del tipo dlgVm dado (usar registro o convención de contenedor o un ContentPresenter con plantillas de datos asociadas al tipo). ShowDialogAsync debería crear una TaskCompletionSource y devolver su .Task proptery. La clase DialogViewModel en sí misma necesita un evento que pueda invocar en la clase derivada cuando desee cerrar, y mirar en la vista de diálogo para cerrar / ocultar el diálogo y completar el TaskCompletionSource.
b) Para usar, simplemente llame a await this.DialogService.ShowDialog (myDlgVm) en su instancia de alguna clase derivada de DialogViewModel. Después de esperar el regreso, observe las propiedades que ha agregado en su máquina virtual de diálogo para determinar qué sucedió; Ni siquiera necesita una devolución de llamada.
VISTA ASISTIDA
Esto hace que tu vista escuche un evento en el modelo de vista. Todo esto podría estar envuelto en un Comportamiento de mezcla para evitar el código detrás y el uso de recursos si así lo desea (FMI, subclase la clase "Comportamiento" para ver una especie de propiedad adjunta Blendable en esteroides). Por ahora, haremos esto manualmente en cada vista:
a) Cree un OpenXXXXXDialogEvent con una carga personalizada (una clase derivada de DialogViewModel).
b) Haga que la vista se suscriba al evento en su evento OnDataContextChanged. Asegúrese de ocultar y cancelar la suscripción si el valor anterior! = Nulo y en el evento Descargado de Windows.
c) Cuando se active 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 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 ventaja agradable es la capacidad de colocar la vista físicamente dentro de una pestaña, por ejemplo. He usado un algoritmo para colocarlo en los límites del control de usuario actual, o si no es lo suficientemente grande, atravesar el árbol visual hasta que se encuentre un contenedor lo suficientemente grande.
Esto permite que los cuadros de diálogo estén cerca del lugar donde se usan realmente, 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 empujar los cuadros de diálogo manualmente, incluso tener múltiples cuasi Los cuadros de diálogo modales se abren en diferentes pestañas o subvistas.
fuente
Use un comando congelable
fuente
Creo que el manejo de un diálogo debería ser responsabilidad de la vista, y la vista debe tener un código que lo respalde.
Si cambia la interacción ViewModel - View para manejar diálogos, entonces ViewModel depende de esa implementación. La forma más sencilla de abordar este problema es hacer que la Vista sea responsable de realizar la tarea. Si eso significa mostrar un 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 todo el punto del patrón MVVM es separar la lógica empresarial de la GUI, por lo que no debería mezclar la lógica GUI (para mostrar un diálogo) en la capa empresarial (ViewModel).
fuente
Una alternativa interesante es usar controladores que son responsables de mostrar las vistas (cuadros de diálogo).
WPF Application Framework (WAF) muestra cómo funciona esto .
fuente
¿Por qué no simplemente generar un evento en la VM y suscribirse al evento en la vista? Esto mantendría la lógica de la aplicación y la vista separadas y aún le permitiría usar una ventana secundaria para los cuadros de diálogo.
fuente
Implementé un comportamiento que escucha un mensaje del 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 inmediato
fuente
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 desencadenante de evento que se suscriba para ver los eventos del modelo y una o más acciones para invocar en respuesta.
fuente
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 se puede extender a cualquier cuadro de diálogo modal, control de exploración de archivos, etc.
fuente
Hice rodar mi propio cargador de ventanas descrito en una respuesta a esta pregunta:
Administrar múltiples vistas WPF en una aplicación
fuente
Karl Shifflett ha creado una aplicación de muestra para mostrar cuadros de diálogo utilizando el enfoque de servicio y el enfoque de solicitud de interacción de prisma.
Me gusta el enfoque de servicio: es menos flexible, por lo que es menos probable que los usuarios rompan algo :) También es coherente con la parte WinForms de mi aplicación (MessageBox.Show) Pero si planea mostrar muchos diálogos diferentes, entonces InteractionRequest es un Mejor manera de ir.
http://karlshifflett.wordpress.com/2010/11/07/in-the-box-ndash-mvvm-training/
fuente
Sé que es una pregunta antigua, pero cuando hice esta búsqueda, encontré muchas preguntas relacionadas, pero no encontré una respuesta realmente clara. ¡Entonces hago mi propia implementación de un cuadro de diálogo / cuadro de mensaje / popin, y lo comparto!
Creo que es "a prueba de MVVM", y trato de hacerlo simple y adecuado, pero soy nuevo en WPF, así que siéntase libre de comentar, o incluso hacer una solicitud de extracción.
https://github.com/Plasma-Paris/Plasma.WpfUtils
Puedes usarlo así:
O así si quieres popin más sofisticado:
Y está mostrando cosas como esta:
fuente
El enfoque estándar
Después de pasar años lidiando con este problema en WPF, finalmente descubrí la forma estándar de implementar diálogos en WPF. Estas son las ventajas de este enfoque:
Entonces, ¿cuál es la clave? Es DI + IoC .
Así es como funciona. Estoy usando MVVM Light, pero este enfoque también puede extenderse a otros marcos:
Agregue una interfaz IDialogService al proyecto VM:
Exponga una propiedad estática pública de
IDialogService
tipo en suViewModelLocator
, pero deje la parte de registro para que realice la capa de Vista. Esta es la clave .Agregue una implementación de esta interfaz en el proyecto de la aplicación.
ShowMessage
,AskBooleanQuestion
etc.), otras son específicas de este proyecto y utilizanWindow
s personalizados . Puede agregar más ventanas personalizadas de la misma manera. La clave es mantener los elementos específicos de la interfaz de usuario en la capa de Vista y simplemente exponer los datos devueltos utilizando POCO en la capa de VM .Realice el registro de IoC en su interfaz en la capa View utilizando esta clase. Puede hacer esto en el constructor de su vista principal (después de la
InitializeComponent()
llamada):Ahí tienes. Ahora tiene acceso a todas sus funciones de diálogo en las capas VM y View. Su capa de VM puede llamar a estas funciones de esta manera:
Otros beneficios gratis
IDialogService
su proyecto de prueba y registrar esa clase en IoC en el constructor de su clase de prueba.Microsoft.Win32
acceder a los cuadros de diálogo Abrir y Guardar. Los he dejado fuera porque también hay una versión WinForms de estos cuadros de diálogo disponible, además de que alguien podría querer crear su propia versión. También tenga en cuenta que algunos de los identificadores utilizadosDialogPresenter
son nombres de mis propias ventanas (por ejemploSettingsWindow
). Deberá eliminarlos tanto de la interfaz como de la implementación o proporcionar sus propias ventanas.DispatcherHelper.Initialize()
inicio del ciclo de vida de su aplicación.Excepto por el
DialogPresenter
que se inyecta en la capa de Vista, se deben registrar otros ViewModalsViewModelLocator
y luego se debe exponer una propiedad estática pública de ese tipo para que la capa de Vista la consuma. Algo como esto:En su mayor parte, sus cuadros de diálogo no deberían tener ningún código subyacente para cosas como el enlace o la configuración de DataContext, etc. Ni siquiera debería pasar cosas como parámetros de constructor. XAML puede hacer todo eso por ti, así:
DataContext
esta manera le brinda todo tipo de beneficios en tiempo de diseño, como Intellisense y autocompletado.Espero que ayude a todos.
fuente
Estaba pensando en un problema similar al preguntar cómo debería ser el modelo de vista para una tarea o diálogo .
Mi solución actual se ve así:
Cuando el modelo de vista decide que se requiere la entrada del usuario, muestra una instancia de
SelectionTaskModel
las posibles opciones para el usuario. La infraestructura se encarga de mostrar la vista correspondiente, que en el momento adecuado llamará a laChoose()
función con la elección del usuario.fuente
Luché con el mismo problema. Se me ocurrió una forma de intercomunicarse entre View y ViewModel. Puede iniciar el envío de un mensaje desde ViewModel a View para indicarle que muestre un cuadro de mensaje e informará de nuevo con el resultado. Entonces ViewModel puede responder al resultado devuelto por la Vista.
Lo demuestro en mi blog :
fuente
Escribí un artículo bastante completo sobre este mismo tema y también desarrollé una biblioteca emergente para cuadros de diálogo MVVM. La estricta adherencia a MVVM no solo es posible, sino que también es muy limpia cuando se implementa correctamente, y puede extenderse fácilmente a bibliotecas de terceros que no se adhieren a ella:
https://www.codeproject.com/Articles/820324/Implementing-Dialog-Boxes-in-MVVM
fuente
Lo siento, pero tengo que intervenir. He pasado por varias de las soluciones sugeridas, antes de encontrar el espacio de nombres Prism.Wpf.Interactivity en el proyecto Prism. Puede usar las solicitudes de interacción y la acción de la ventana emergente para desplegar una ventana personalizada o para necesidades más simples, hay ventanas emergentes de notificación y confirmación integradas. Estos crean ventanas verdaderas y se administran como tales. puede pasar un objeto de contexto con cualquier dependencia que necesite en el cuadro de diálogo. Utilizamos esta solución en mi trabajo desde que la encontré. Tenemos numerosos desarrolladores senior aquí y a nadie se le ocurrió nada mejor. Nuestra solución anterior era el servicio de diálogo en una superposición y el uso de una clase de presentador para hacerlo posible, pero tenía que tener fábricas para todos los modelos de vista de diálogo, etc.
Esto no es trivial, pero tampoco es súper complicado. Y está integrado en Prism y, por lo tanto, es la mejor (o mejor) práctica en mi humilde opinión.
¡Mis 2 centavos!
fuente
EDITAR: sí, estoy de acuerdo en que este no es un enfoque MVVM correcto y ahora estoy usando algo similar a lo sugerido por blindmeis.
Una de las formas en que podrías hacer esto es
En su Modelo de vista principal (donde abre el modal):
Y en su ventana de vista modal / modelo de vista:
XAML:
ViewModel:
o similar a lo que se publica aquí WPF MVVM: Cómo cerrar una ventana
fuente