Estoy tratando de aprender WPF y el problema MVVM, pero he encontrado un obstáculo. Esta pregunta es similar pero no es exactamente la misma que esta (manejo-diálogos-en-wpf-con-mvvm) ...
Tengo un formulario de "Inicio de sesión" escrito con el patrón MVVM.
Este formulario tiene un ViewModel que contiene el Nombre de usuario y la Contraseña, que están vinculados a la vista en XAML utilizando enlaces de datos normales. También tiene un comando "Iniciar sesión" que está vinculado al botón "Iniciar sesión" en el formulario, de nuevo usando el enlace de datos normal.
Cuando se activa el comando "Iniciar sesión", invoca una función en ViewModel que se apaga y envía datos a través de la red para iniciar sesión. Cuando se completa esta función, hay 2 acciones:
El inicio de sesión no era válido: solo mostramos un MessageBox y todo está bien
El inicio de sesión fue válido, debemos cerrar el formulario de inicio de sesión y hacer que regrese verdadero como su
DialogResult
...
El problema es que ViewModel no sabe nada sobre la vista real, entonces, ¿cómo puede cerrar la vista y decirle que devuelva un DialogResult particular? Podría pegar algo de código en CodeBehind, y / o pasar la vista al ViewModel, pero parece que podría derrotar por completo el punto de MVVM ...
Actualizar
Al final, simplemente violé la "pureza" del patrón MVVM e hice que View publicara un Closed
evento y exponga un Close
método. ViewModel simplemente llamaría view.Close
. La vista solo se conoce a través de una interfaz y se conecta a través de un contenedor IOC, por lo que no se pierde capacidad de prueba ni mantenimiento.
¡Parece bastante tonto que la respuesta aceptada sea de -5 votos! Si bien soy consciente de los buenos sentimientos que uno tiene al resolver un problema mientras es "puro", seguramente no soy el único que piensa que 200 líneas de eventos, comandos y comportamientos solo para evitar un método de una línea en el nombre de "patrones" y "pureza" es un poco ridículo ...
Close
método simple sigue siendo la mejor solución. Todo lo demás en los otros diálogos más complejos es MVVM y databound, pero parecía una tontería implementar las enormes "soluciones" aquí en lugar de solo un método simple ...Respuestas:
Me inspiró la respuesta de Thejuan para escribir una propiedad adjunta más simple. Sin estilos, sin desencadenantes; en cambio, puedes hacer esto:
Esto es casi tan limpio como si el equipo de WPF lo hubiera hecho bien e hiciera de DialogResult una propiedad de dependencia en primer lugar. Simplemente coloque una
bool? DialogResult
propiedad en su ViewModel e implemente INotifyPropertyChanged, y listo, su ViewModel puede cerrar la Ventana (y establecer su DialogResult) simplemente configurando una propiedad. MVVM como debería ser.Aquí está el código para DialogCloser:
También publiqué esto en mi blog .
fuente
Desde mi punto de vista, la pregunta es bastante buena, ya que el mismo enfoque se usaría no solo para la ventana "Iniciar sesión", sino para cualquier tipo de ventana. He revisado muchas sugerencias y ninguna está bien para mí. Revise mi sugerencia que se tomó del artículo del patrón de diseño MVVM .
Cada clase ViewModel debe heredar de
WorkspaceViewModel
que tiene elRequestClose
evento y laCloseCommand
propiedad delICommand
tipo. La implementación predeterminada de laCloseCommand
propiedad generará elRequestClose
evento.Para cerrar la
OnLoaded
ventana, debe anularse el método de su ventana:o
OnStartup
método de su aplicación:Supongo que la implementación de
RequestClose
eventos yCloseCommand
propiedades en elWorkspaceViewModel
es bastante clara, pero les mostraré que son consistentes:Y el código fuente de
RelayCommand
:PD: ¡No me traten mal por esas fuentes! Si los tuviera ayer eso me habría ahorrado unas horas ...
PPS Cualquier comentario o sugerencia es bienvenida.
fuente
customer.RequestClose
en el código detrás de tu archivo XAML no viola el patrón MVVM? En primer lugar, también podría vincularse alClick
controlador de eventos en su botón de cierre al ver que de todos modos tocó el código e hizo unthis.Close()
! ¿Correcto?Usé comportamientos adjuntos para cerrar la ventana. Enlace una propiedad de "señal" en su ViewModel al comportamiento adjunto (en realidad uso un disparador) Cuando se establece en verdadero, el comportamiento cierra la ventana.
http://adammills.wordpress.com/2009/07/01/window-close-from-xaml/
fuente
Hay muchos comentarios que discuten los pros y los contras de MVVM aquí. Para mí, estoy de acuerdo con Nir; es cuestión de usar el patrón de manera adecuada y MVVM no siempre encaja. La gente parece estar dispuesta a sacrificar todos los principios más importantes del diseño de software SOLO para que se ajuste a MVVM.
Dicho esto, creo que su caso podría encajar bien con un poco de refactorización.
En la mayoría de los casos que he encontrado, WPF le permite pasar SIN múltiples correos electrónicos
Window
. Tal vez usted podría tratar de usarFrame
s yPage
s en lugar de Windows conDialogResult
s.En su caso, mi sugerencia sería
LoginFormViewModel
manejar elLoginCommand
y, si el inicio de sesión no es válido, establezca una propiedad enLoginFormViewModel
un valor apropiado (false
o algún valor de enumeración comoUserAuthenticationStates.FailedAuthentication
). Haría lo mismo para un inicio de sesión exitoso (true
o algún otro valor de enumeración). Luego usaría unDataTrigger
que responde a los diversos estados de autenticación de usuario y podría usar un simpleSetter
para cambiar laSource
propiedad delFrame
.Hacer que su ventana de inicio de sesión regrese
DialogResult
creo que es donde se está confundiendo; esoDialogResult
es realmente una propiedad de su ViewModel. En mi experiencia, ciertamente limitada, con WPF, cuando algo no se siente bien, generalmente porque estoy pensando en términos de cómo habría hecho lo mismo en WinForms.Espero que ayude.
fuente
Suponiendo que su diálogo de inicio de sesión es la primera ventana que se crea, intente esto dentro de su clase LoginViewModel:
fuente
Esta es una solución simple y limpia: agrega un evento a ViewModel e indica a la ventana que se cierre cuando se activa ese evento.
Para obtener más detalles, consulte mi publicación de blog, Cerrar ventana de ViewModel .
XAML:
ViewModel:
Nota: El ejemplo usa Prism's
DelegateCommand
(ver Prism: Command ), pero cualquierICommand
implementación puede usarse para ese asunto.Puede usar comportamientos de este paquete oficial.
fuente
La forma en que lo manejaría es agregar un controlador de eventos en mi ViewModel. Cuando el usuario iniciaba sesión correctamente, activaba el evento. En mi vista, me adjuntaría a este evento y, cuando se disparara, cerraría la ventana.
fuente
Esto es lo que hice inicialmente, que funciona, sin embargo, parece bastante largo y feo (todo estático global nunca es bueno)
1: App.xaml.cs
2: LoginForm.xaml
3: LoginForm.xaml.cs
4: LoginFormViewModel.cs
Más tarde, eliminé todo este código y acabo de
LoginFormViewModel
llamar al método Cerrar en su vista. Terminó siendo mucho más agradable y fácil de seguir. En mi humilde opinión, el objetivo de los patrones es dar a las personas una forma más fácil de entender lo que está haciendo su aplicación, y en este caso, MVVM estaba haciendo que sea mucho más difícil de entender que si no la hubiera usado, y ahora era un anti- patrón.fuente
Para su información, me encontré con este mismo problema y creo que descubrí una solución que no requiere estáticos ni estadísticas, aunque puede que no sea la mejor respuesta. Dejo que ustedes decidan eso por ustedes mismos.
En mi caso, el ViewModel que crea una instancia de la ventana que se mostrará (llamémoslo ViewModelMain) también conoce el LoginFormViewModel (usando la situación anterior como ejemplo).
Entonces, lo que hice fue crear una propiedad en el LoginFormViewModel que era del tipo ICommand (vamos a llamarlo CloseWindowCommand). Luego, antes de llamar a .ShowDialog () en la ventana, configuro la propiedad CloseWindowCommand en LoginFormViewModel en el método window.Close () de la ventana que ejecuté. Luego, dentro de LoginFormViewModel, todo lo que tengo que hacer es llamar a CloseWindowCommand.Execute () para cerrar la ventana.
Supongo que es un poco como una solución / truco, pero funciona bien sin realmente romper el patrón MVVM.
No dudes en criticar este proceso tanto como quieras, ¡puedo soportarlo! :)
fuente
Probablemente sea muy tarde, pero me encontré con el mismo problema y encontré una solución que me funciona.
No puedo entender cómo crear una aplicación sin diálogos (tal vez es solo un bloqueo mental). Así que estaba en un punto muerto con MVVM y mostrando un diálogo. Entonces me encontré con este artículo de CodeProject:
http://www.codeproject.com/KB/WPF/XAMLDialog.aspx
Que es un UserControl que básicamente permite que una ventana esté dentro del árbol visual de otra ventana (no permitido en xaml). También expone una propiedad de dependencia booleana llamada IsShowing.
Puede establecer un estilo como, generalmente en una biblioteca de recursos, que básicamente muestra el cuadro de diálogo siempre que la propiedad Contenido del control! = Nulo a través de activadores:
En la vista donde desea mostrar el cuadro de diálogo, simplemente tenga esto:
Y en su ViewModel todo lo que tiene que hacer es establecer la propiedad en un valor (Nota: la clase ViewModel debe admitir INotifyPropertyChanged para que la vista sepa que algo sucedió).
al igual que:
Para hacer coincidir el modelo de vista con la vista, debe tener algo como esto en una biblioteca de recursos:
Con todo eso, obtienes un código de una línea para mostrar el diálogo. El problema que obtienes es que realmente no puedes cerrar el diálogo solo con el código anterior. Es por eso que debe poner un evento en una clase base ViewModel de la cual DisplayViewModel hereda y, en lugar del código anterior, escriba esto
Luego puede manejar el resultado del diálogo a través de la devolución de llamada.
Esto puede parecer un poco complejo, pero una vez que se sientan las bases, es bastante sencillo. Nuevamente, esta es mi implementación, estoy seguro de que hay otros :)
Espero que esto ayude, me salvó.
fuente
Ok, entonces esta pregunta tiene casi 6 años y todavía no puedo encontrar aquí lo que creo que es la respuesta correcta, así que permítanme compartir mis "2 centavos" ...
De hecho, tengo 2 formas de hacerlo, la primera es la simple ... la segunda en la correcta, por lo que si está buscando la correcta, omita el n. ° 1 y salte al n. ° 2 :
1. Rápido y fácil (pero no completo)
Si solo tengo un pequeño proyecto, a veces solo creo una CloseWindowAction en ViewModel:
Y quien crea la Vista, o en el código de la Vista detrás, acabo de configurar el Método que la Acción llamará:
(recuerde que MVVM se trata de la separación de la vista y el modelo de vista ... el código de la vista detrás sigue siendo la vista y siempre que haya una separación adecuada, no está violando el patrón)
Si algún ViewModel crea una nueva ventana:
O si lo desea en su ventana principal, simplemente colóquelo debajo del constructor de su vista:
cuando desee cerrar la ventana, simplemente llame a la Acción en su ViewModel.
2. La forma correcta
Ahora, la forma correcta de hacerlo es utilizando Prism (en mi humilde opinión), y todo se puede encontrar aquí .
Puede realizar una Solicitud de interacción , completarla con los datos que necesitará en su nueva ventana, almorzarla, cerrarla e incluso recibir datos nuevamente . Todo esto encapsulado y MVVM aprobado. Incluso obtiene un estado de cómo se cerró la ventana , como si el usuario
Canceled
oAccepted
(botón OK) la ventana y los datos vuelvan si lo necesita . Es un poco más complicado y la respuesta n. ° 1, pero es mucho más completo y es un patrón recomendado por Microsoft.El enlace que proporcioné tiene todos los fragmentos de código y ejemplos, por lo que no me molestaré en colocar ningún código aquí, solo lea el artículo de descarga del Prism Quick Start y ejecútelo, es realmente simple entender un poco más detallado hacer que funcione, pero los beneficios son mayores que simplemente cerrar una ventana.
fuente
+=
para agregar un delegado y llame a la Acción, los activará a todos ... O lo hará tiene que hacer una lógica especial en su VM para que sepa qué ventana cerrar (tal vez tenga una colección de Acciones de cierre) ... Pero creo que tener varias vistas vinculadas a una VM no es la mejor práctica, es es mejor administrar tener una vista y una instancia de máquina virtual, unidas entre sí y tal vez una máquina virtual principal que administre todas las máquinas virtuales secundarias que están vinculadas a todas las vistas.fuente
Puede hacer que ViewModel exponga un evento en el que la Vista se registre. Luego, cuando ViewModel decide su hora de cerrar la vista, dispara ese evento que hace que la vista se cierre. Si desea que se devuelva un valor de resultado específico, entonces tendría una propiedad en ViewModel para eso.
fuente
Solo para agregar a la gran cantidad de respuestas, quiero agregar lo siguiente. Suponiendo que tiene un ICommand en su ViewModel y desea que ese comando cierre su ventana (o cualquier otra acción), puede usar algo como lo siguiente.
No es perfecto, y puede ser difícil de probar (ya que es difícil burlarse de una estática) pero es más limpio (en mi humilde opinión) que las otras soluciones.
Erick
fuente
Implementé la solución de Joe White, pero me encontré con problemas ocasionales " DialogResult solo se puede configurar después de que se crea la ventana y se muestra como errores de diálogo ".
Mantuve el ViewModel alrededor después de que se cerró la Vista y ocasionalmente abrí una nueva Vista usando la misma VM. Parece que al cerrar la nueva Vista antes de que la Vista anterior se hubiera recolectado basura, DialogResultChanged intentó establecer la propiedad DialogResult en la ventana cerrada, provocando así el error.
Mi solución fue cambiar DialogResultChanged para verificar la propiedad IsLoaded de la ventana :
Después de hacer este cambio, se ignoran los archivos adjuntos a los diálogos cerrados.
fuente
Terminé combinando la respuesta de Joe White y algo de código de la respuesta de Adam Mills , ya que necesitaba mostrar un control de usuario en una ventana creada mediante programación. Por lo tanto, DialogCloser no necesita estar en la ventana, puede estar en el control del usuario.
Y DialogCloser encontrará la ventana del control del usuario si no estaba conectada a la ventana misma.
fuente
El comportamiento es la forma más conveniente aquí.
Por un lado, se puede vincular al modelo de vista dado (que puede indicar "¡cierra el formulario!")
Por otro lado, tiene acceso al formulario en sí mismo, por lo que puede suscribirse a los eventos específicos del formulario necesarios, o mostrar el diálogo de confirmación, o cualquier otra cosa.
Escribir el comportamiento necesario puede verse aburrido desde la primera vez. Sin embargo, a partir de ahora, puede reutilizarlo en cada formulario que necesite mediante un fragmento XAML de una sola línea. Y si es necesario, puede extraerlo como un ensamblaje separado para que pueda incluirse en el próximo proyecto que desee.
fuente
¿Por qué no simplemente pasar la ventana como parámetro de comando?
C#:
XAML:
fuente
Window
tipo que no sea MVVM "puro". Vea esta respuesta, donde la VM no está restringida a unWindow
objeto.Otra solución es crear una propiedad con INotifyPropertyChanged en View Model como DialogResult, y luego en Code Behind escriba esto:
El fragmento más importante es
_someViewModel_PropertyChanged
.DialogResultPropertyName
puede ser una cadena const públicaSomeViewModel
.Utilizo este tipo de truco para hacer algunos cambios en los controles de vista en caso de que sea difícil hacerlo en ViewModel. OnPropertyChanged en ViewModel puede hacer lo que quiera en View. ViewModel todavía es 'comprobable por unidad' y algunas pequeñas líneas de código en el código detrás no hacen ninguna diferencia.
fuente
Yo iría de esta manera:
fuente
He leído todas las respuestas, pero debo decir que la mayoría de ellas no son lo suficientemente buenas o incluso peores.
Puede manejar esto hermosamente con la clase DialogService , cuya responsabilidad es mostrar la ventana de diálogo y devolver el resultado del diálogo. He creado un proyecto de muestra que demuestra su implementación y uso.
Aquí están las partes más importantes:
¿No es esto más simple? ¿Más directo, más legible y, por último, pero no menos fácil de depurar que EventAggregator u otras soluciones similares?
como puede ver, en mis modelos de vista he usado el primer enfoque de ViewModel descrito en mi publicación aquí: Mejores prácticas para llamar a View desde ViewModel en WPF
Por supuesto, en el mundo real,
DialogService.ShowDialog
deben tener más opciones para configurar el diálogo, por ejemplo, botones y comandos que deben ejecutar. Hay diferentes formas de hacerlo, pero está fuera de alcance :)fuente
Si bien esto no responde a la pregunta de cómo hacer esto a través del modelo de vista, sí muestra cómo hacerlo usando solo XAML + el SDK de mezcla.
Elegí descargar y usar dos archivos del SDK de Blend, que puedes usar como paquete de Microsoft a través de NuGet. Los archivos son:
System.Windows.Interactivity.dll y Microsoft.Expression.Interactions.dll
Microsoft.Expression.Interactions.dll le brinda buenas capacidades, como la capacidad de establecer propiedades o invocar un método en su modelo de vista u otro objetivo y también tiene otros widgets dentro.
Algunos XAML:
Tenga en cuenta que si solo desea un comportamiento simple de Aceptar / Cancelar, puede escapar usando las propiedades IsDefault e IsCancel siempre que la ventana se muestre con Window.ShowDialog ().
Personalmente tuve problemas con un botón que tenía la propiedad IsDefault establecida en verdadero, pero estaba oculta cuando se cargaba la página. No parecía querer jugar bien después de que se mostró, así que solo estoy configurando la propiedad Window.DialogResult como se muestra arriba y funciona para mí.
fuente
Aquí está la solución simple libre de errores (con código fuente), está funcionando para mí.
Derive su ViewModel de
INotifyPropertyChanged
Crear una propiedad observable CloseDialog en ViewModel
}
Adjunte un controlador en la vista para este cambio de propiedad
Ahora ya casi has terminado. En caso de que el controlador haga
DialogResult = true
fuente
Cree un
Dependency Property
en suView
/ anyUserControl
(oWindow
desea cerrar). Como abajo:Y vincúlelo desde la propiedad de su ViewModel :
Propiedad en
VeiwModel
:Ahora active la operación de cierre cambiando el
CloseWindow
valor en ViewModel. :)fuente
Cuando necesite cerrar la ventana, simplemente ponga esto en el modelo de vista:
ta-da
fuente
¡Eso es suficiente!
fuente