El manejo de los Diálogos en WPF con MVVM

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

¿Alguien sabe de una buena manera de manejar los resultados de los diálogos? Estoy hablando de los cuadros de diálogo de windows, tales como el cuadro de mensaje.

Una de las maneras de hacerlo fue un evento en el viewmodel que el punto de vista suscribirse a cuando un cuadro de diálogo que se requiere.

public event EventHandler<MyDeleteArgs> RequiresDeleteDialog;

Esto está bien, pero significa que el punto de vista requiere que el código que es algo que me gustaría alejarse.

  • Por qué no se unen a un ayudante objeto en la Vista?
  • No estoy seguro de lo que quieres decir.
  • Si entiendo la pregunta, usted no desea que la máquina virtual apareciendo cuadros de diálogo, y no desea que el código subyacente en la Vista. Además parece que prefieren los comandos de eventos. Estoy de acuerdo con todos ellos, por lo que el uso de una clase auxiliar en el punto de Vista que expone un comando para controlar el cuadro de diálogo. He contestado a esta pregunta en otro hilo aquí: stackoverflow.com/a/23303267/420400. Sin embargo, la última frase la hace sonar como si usted no quiere nada de código, cualquier lugar de la Vista. Entiendo que la preocupación, pero el código en cuestión es sólo un condicional, y no parece que vaya a cambiar.
  • Thje modelo de vista siempre debe ser responsable por la lógica detrás de la creación del cuadro de diálogo, que es la razón de su existencia en el primer lugar. Que dijo que no (y no debe) hacer el levantamiento de elevación de la creación de la propia vista. Escribí un artículo sobre este tema en codeproject.com/Articles/820324/… donde puedo muestran que el ciclo de vida de los cuadros de diálogo pueden ser gestionados a través del enlace de datos de WPF y sin romper el patrón MVVM.
InformationsquelleAutor Ray Booysen | 2009-01-18

23 Kommentare

  1. 131

    Me sugieren la necesidad de renunciar a la década de 1990 los cuadros de diálogo modales y en lugar de la implementación de un control como una superposición (canvas+posicionamiento absoluto) con visibilidad atado a un valor booleano de vuelta en la máquina virtual. Más cerca de un tipo ajax control.

    Esto es muy útil:

    <BooleanToVisibilityConverter x:Key="booltoVis" />

    como en:

    <my:ErrorControl Visibility="{Binding Path=ThereWasAnError, Mode=TwoWay, Converter={StaticResource booltoVis}, UpdateSourceTrigger=PropertyChanged}"/>

    He aquí cómo he implementado como un control de usuario. Haga clic en la » x » se cierra el control en una línea de código en el código del usercontrol detrás. (Ya tengo mis puntos de vista .exe y Viewmodel en una dll, no me siento mal sobre el código que manipula interfaz de usuario.)

    El manejo de los Diálogos en WPF con MVVM

    • Sí, me gusta esta idea, pero también quisiera ver algunos ejemplo de este control en términos de cómo mostrar y recuperar resultado del diálogo de él, etc. Especialmente en MVVM escenario en Silverlight.
    • ¿Cómo se puede evitar que el usuario de interactuar con los controles debajo de este cuadro de diálogo de superposición?
    • Gran idea. He creado un menú Emergente y controla la visibilidad mediante la unión a IsOpen.
    • «¿Cómo se puede evitar que el usuario de interactuar con los controles …» ? : private void Window_PreviewKeyDown …. e.Handled = true;
    • No sé si Jeffrey publicado su solución en cualquier otro lugar, pero me dio un tiro en esto. Hice mi propio UserControl y aplicado exactamente como Jeffrey XAML. Para obtener un resultado del diálogo, yo uso RelayCommand y uso CommandParameters para pasar un Sí/No el resultado
    • El problema con este enfoque es que no se puede abrir un segundo cuadro de diálogo modal de la primera, al menos no sin fuertes modificaciones en el sistema de revestimiento…
    • Y otro problema con este enfoque es el uso de código detrás de la cual hay disminución de mantenimiento. En todos mis proyectos, yo trato de evitar en la medida de lo posible mediante el código de atrás ( además de los casos cuando no tengo otra opción ).
    • cuando puedo evitar que los usuarios interactúan con los controles como este – private void Window_PreviewMouseDown .... e.Handled = true;, también se deshabilita el control de errores, por lo que el usuario cant cerrar el mensaje de error tampoco. ¿Cómo puedo resolver este problema? También, ¿cómo hacer que el resto de la ventana gris/apagado cuando aparezca el error. Thx. Me encanta esta solución.
    • Otro problema con este enfoque es que el «diálogo» no puede ser movido. En nuestras aplicaciones, debemos tener un mueble de diálogo para que el usuario pueda ver lo que está detrás de él.
    • Este enfoque parece terrible para mí. Lo que me estoy perdiendo? Cómo es esto mejor que el de un cuadro de diálogo?
    • «¿Cómo se puede evitar que el usuario de interactuar con los controles debajo de este cuadro de diálogo de superposición? » en lugar de la captura de eventos de la solución, se podría dar el diálogo con un 100% de la anchura/altura de fondo. Si usted no quiere que sea visible, sin embargo, la configuración de la bg transparente en lugar de null no vamos a ningún evento de flujo a través. Sin embargo, uno todavía tiene que prestar atención a, por ejemplo, los eventos clave, en el caso de un botón o algo en el oscurecido se tiene el enfoque.
    • Otro problema es que cuando tienes dos monitores y quieren arrastrar el cuadro de diálogo para el segundo.
    • O en cualquier lugar fuera de los límites de la ventana padre, para que la materia.
    • «canvas+posicionamiento absoluto» – ¿qué? Por qué? La mera mención de «posicionamiento absoluto» es, en la gran mayoría de los casos, un signo de advertencia de que algo está muy, muy mal. ¿Cómo sería una posición absoluta ser útil si la ventana principal puede tener cualquier tamaño? En general, me parece que esta solución se esfuerza para replicar cada aspecto de lo limitado de la web de la UIs que el uso de soluciones para un aspecto real de GUIs con verdaderos cuadros de diálogo.

  2. 51

    Usted debe utilizar un mediador para esto.
    El mediador es un común patrón de diseño conocido también como Mensajero en algunas de sus implementaciones.
    Es un paradigma de tipo de Registro/Notificar y permite a su ViewModel y Vistas a comunicarse a través de una baja-junto de mensajería mecanismos.

    Usted debe comprobar fuera de la google WPF grupo de Discípulos, y sólo la búsqueda de Mediador.
    Usted será mucho más feliz con las respuestas…

    Sin embargo, puede comenzar con esto:

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

    Disfrutar !

    Edit: se puede ver la respuesta a este problema con MVVM Light Toolkit aquí:

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

    • Marlon grech ha acaba de publicar una nueva aplicación del mediador: marlongrech.wordpress.com/2009/04/16/…
    • Sólo una observación : el modelo de Mediador no fue introducido por el WPF Discípulos, es un clásico de GoF patrón… (dofactory.com/Patterns/PatternMediator.aspx). Bonita respuesta de otra manera 😉
    • Por favor dios, no el uso de un mediador o a un maldito messenger. Que tipo de código con decenas de mensajes que vuelan alrededor se vuelve muy difícil de depurar a menos que de alguna manera se puede recordar todos los muchos puntos en toda su base de código que se suscriban y el manejo de cada caso. Se convierte en una pesadilla para los nuevos desarrolladores. De hecho, considero que todo el MvvMLight biblioteca a ser una lucha masiva contra el patrón para su penetrante y el uso innecesario de asincrónica de mensajería. La solución es simple: llamar a un cuadro de diálogo separado del servicio (es decir, IDialogService) de su diseño. La interfaz tiene métodos y eventos para las devoluciones de llamada.
  3. 29

    Una buena MVVM diálogo debe:

    1. Ser declarado con sólo XAML.
    2. Obtener todos los de su comportamiento desde el enlace de datos.

    Por desgracia, WPF no proporcionan estas características. Muestra un cuadro de diálogo que requiere de un código-detrás de la llamada a ShowDialog(). La Ventana de la clase, que apoya los diálogos, no pueden ser declaradas en XAML, por lo que no puede ser fácilmente enlace de datos con el DataContext.

    Para solucionar esto, escribí un XAML talón de control que se encuentra en el árbol lógico y relés de enlace de datos para una Ventana y asas de mostrar y ocultar el cuadro de diálogo. Usted puede encontrar aquí: http://www.codeproject.com/KB/WPF/XAMLDialog.aspx

    Es algo sencillo de usar y no requiere ningún extraño changs a su ViewModel y no requieren de eventos o mensajes. La llamada básicas se parece a esto:

    <dialog:Dialog Content="{Binding Path=DialogViewModel}" Showing="True" />

    Usted probablemente desea agregar un estilo que establece que Muestra. Lo explico en mi artículo. Espero que esto ayude.

    • Ese es realmente un enfoque interesante para el problema de mostrar las ventanas de diálogo en MVVM.
    • "Showing a dialog requires a code-behind" mmm puede llamar a que en ViewModel
    • Me gustaría añadir en el punto 3 – usted es libre de unirse a otros objetos dentro de la vista. Dejando el cuadro de diálogo del código detrás de vacío significa que no hay ningún código de C# en cualquier lugar de la vista, y el enlace de datos no implica la unión a la VM.
  4. 23

    Yo uso este enfoque para los diálogos con MVVM.

    Todo lo que tengo que hacer ahora es llamar a la siguiente, desde mi punto de vista modelo.

    var result = this.uiDialogService.ShowDialog("Dialogwindow title goes here", dialogwindowVM);
    • Debe ser la respuesta.
    • la biblioteca no uiDialogService viene de?
    • ninguna biblioteca. es sólo una pequeña interfaz y la implementación: stackoverflow.com/questions/3801681/… . para ser justos cajeros automáticos que tiene algo más de sobrecargas para mis necesidades 🙂 (altura, anchura, propertysettings y así sucesivamente)
  5. 16

    Mi solución actual resuelve la mayoría de las cuestiones que usted ha mencionado aún su completamente abstraído de la plataforma de cosas específicas y pueden ser reutilizados.
    También he utilizado ningún código-sólo por detrás de unión con DelegateCommands que implementar ICommand.
    Diálogo es, básicamente, una Vista de un control independiente que tiene su propia ViewModel y se muestra desde la Perspective de la pantalla principal pero que se activan desde la interfaz de usuario a través de DelagateCommand de unión.

    Ver completo Silverlight 4 solución aquí Los cuadros de diálogo modales con MVVM y Silverlight 4

    • Igual que @Elad Katz enfoque, su respuesta carece de los contenidos vinculados – por favor, mejorar su respuesta mediante la inserción como eso es lo que se considera una buena respuesta de aquí a ENTONCES. No obstante, gracias por tu aporte! 🙂
  6. 6

    Tuve que lidiar con este concepto por un tiempo al aprendizaje (sigo aprendiendo) MVVM. Lo que me decidí, y lo que creo que los demás ya se ha decidido, pero que no estaba claro para mí es esta:

    Mi idea original era que un ViewModel no se debe permitir que un cuadro de diálogo directamente ya que no tiene nada que decidir cómo aparecerá un cuadro de diálogo aparecerá. Dado a esto, empecé a pensar en cómo podía pasar mensajes tanto como me gustaría tener en MVP (es decir, Ver.ShowSaveFileDialog()). Sin embargo, creo que este es el enfoque equivocado.

    Está bien para un ViewModel para llamar a un diálogo directamente. Sin embargo, cuando usted está probando un ViewModel , que significa que el cuadro de diálogo de cualquiera de los pop-up durante la prueba, o no todos juntos (nunca trataron de esto).

    Así que, ¿qué tiene que ocurrir es que si bien la prueba es el uso de una «prueba» de la versión de su cuadro de diálogo. Esto significa que, para siempre, de diálogo usted tiene, usted necesita para crear una Interfaz y bien burlarse de el cuadro de diálogo de respuesta o crear una prueba de simulacro que tendrá un comportamiento por defecto.

    Usted ya debe estar utilizando algún tipo de Servicio de Localizador de o Cio que se puede configurar para proporcionar la versión correcta según el contexto.

    El uso de este enfoque, el ViewModel es todavía comprobable y dependiendo de cómo te burlas de tu diálogos, usted puede controlar el comportamiento.

    Espero que esto ayude.

  7. 5

    Hay dos buenas maneras de hacer esto, 1) un servicio de diálogo (fácil de limpiar), y 2) ver asistida. Ver asistida proporciona algunas características, pero por lo general no la pena.

    SERVICIO DE DIÁLOGO

    a) un cuadro de diálogo de la interfaz del servicio como a través de constructor o alguna dependencia contenedor:

    interface IDialogService
    {
    Task ShowDialogAsync(DialogViewModel dlgVm);
    }

    b) la implementación de IDialogService debe abrir una ventana (o inyectar un poco de control en la ventana activa), crear una vista que corresponde al nombre de la dlgVm tipo (el envase de uso de registro de la convención o un ContentPresenter con el tipo asociado DataTemplates). ShowDialogAsync debe crear un TaskCompletionSource y devolver su .Tarea proptery. El DialogViewModel clase en sí misma las necesidades de un evento puede invocar en la clase derivada cuando se quiere cerrar, y ver en la vista de cuadro de diálogo para cerrar/ocultar el cuadro de diálogo y completar el TaskCompletionSource.

    b) utilizar, simplemente llame esperan.DialogService.ShowDialog(myDlgVm) en la instancia de algunos DialogViewModel de la clase derivada. Después de aguardar devoluciones, mira en propiedades que hemos añadido en el cuadro de diálogo de VM para determinar lo sucedido; ni siquiera necesita una devolución de llamada.

    VER ASISTIDA

    Esto tiene su punto de vista de escuchar a un evento en el viewmodel. Todo esto podría ser envuelto en una Mezcla de Comportamiento para evitar el código detrás del uso de los recursos y si usted está tan inclinado (FMI, subclase del «Comportamiento» de la clase a ver una especie de capacidad de difuminado de la propiedad asociada a los esteroides). Por ahora, vamos a hacer esto de forma manual en cada vista:

    a) Crear un OpenXXXXXDialogEvent con una carga personalizada (un DialogViewModel clase derivada).

    b) Tener la vista suscribirse al evento en su OnDataContextChanged evento. Asegúrese de ocultar y darse de baja si el valor anterior != null y en la Ventana de la Descarga evento.

    c) Cuando se activa el evento, tienen la opinión de abrir su punto de vista, que podría ser en un recurso en su página, o usted podría encontrar que, por convención, en otros lugares (como en el cuadro de diálogo enfoque de servicio).

    Este enfoque es más flexible, pero requiere más trabajo a utilizar. Yo no lo uso mucho. Bueno ventaja es la capacidad para colocar la vista físicamente dentro de una pestaña, por ejemplo. He utilizado un algoritmo para colocarlo en el usuario actual del control de los límites, o si no es lo suficientemente grande, recorrer el árbol visual hasta un recipiente lo suficientemente grande como se encuentra.

    Esto permite a los cuadros de diálogo para estar cerca del lugar en el que estamos, en realidad, sólo tenue de la parte de la aplicación relacionados con la actividad actual, y permiten al usuario moverse dentro de la aplicación sin tener que empuje manualmente los diálogos de distancia, incluso tener varias cuasi-los cuadros de diálogo modales abiertos en diferentes pestañas o sub-vistas.

    • Un servicio de diálogo es mucho más fácil, sin duda, y lo que suele hacer. También hace que sea fácil para cerrar la vista del cuadro de diálogo de la vista principal del modelo, el cual es necesario cuando el padre de familia modelo de vista es el cierre o cancelación.
  8. 4

    Utilizar un comando congelar

    <Grid>
            <Grid.DataContext>
                <WpfApplication1:ViewModel />
            </Grid.DataContext>
    
    
            <Button Content="Text">
                <Button.Command>
                    <WpfApplication1:MessageBoxCommand YesCommand="{Binding MyViewModelCommand}" />
                </Button.Command>
            </Button>
    
    </Grid>
    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();
        }
    }
    • Este código necesita algo de trabajo, pero es la mejor idea, por mucho, especialmente para los cuadros de diálogo del sistema, tales como el archivo o impresora diálogos. Cuadros de diálogo de pertenecer a Ver si hay algo. Para el archivo de los diálogos, el resultado (nombre de archivo seleccionado) puede ser transmitido al interior de comandos como parámetro.
  9. 3

    Creo que el manejo de un cuadro de diálogo debe ser la responsabilidad de la vista y el punto de vista de las necesidades de tener un código para apoyar eso.

    Si cambia el ViewModel de Vista de la interacción para manejar los diálogos, a continuación, el ViewModel es depende de que aplicación. La forma más sencilla de lidiar con este problema es hacer que la Vista se encarga de realizar la tarea. Si eso significa que muestra un cuadro de diálogo que entonces está bien, pero también podría ser un mensaje de estado en la barra de estado, etc.

    Mi punto es que el punto central de todo el patrón MVVM es separar la lógica de negocio de la interfaz gráfica de usuario, por lo que no debe ser la mezcla de la GUI de la lógica (para mostrar un cuadro de diálogo) en la capa de negocio (ViewModel).

    • La VM nunca iba a manejar el diálogo, en mi ejemplo sería simpy tener un evento que requeriría el cuadro de diálogo para el fuego y pase info en alguna forma de EventArgs. Si la vista es el responsable, ¿cómo se pasa de nuevo la información de la VM?
    • Dicen que la VM necesita para eliminar algo. La VM llama a un método en la Vista de Borrar que devuelve un valor booleano. La Vista puede eliminar el elemento directamente y devolver true, o mostrar un cuadro de diálogo de confirmación y devolver true/false dependiendo de la respuesta a los usuarios.
    • La VM no sabe nada sobre el diálogo, pero sólo pidió a la vista para eliminar algo, que la vista sea confirmado o negado.
    • Siempre he pensado que el punto de MVVM fue Modelo: lógica de negocio, Perspective: interfaz de usuario de la lógica y de la Vista: no hay lógica. Que de alguna manera contradice a su último párrafo. Por favor explique!
    • Se pide a la vista para eliminar algo? Seguramente la otra manera alrededor.
    • Soy un principiante en relación con la aplicación del patrón MVVM sin embargo creo que es un error pensar en el ViewModel como una capa de negocio. En su lugar, mi entendimiento es que el ViewModel mantiene la lógica de presentación y pistas de estado necesario para la interfaz de usuario. En mi opinión, el Modelo es el lugar donde la lógica de negocio y el modelo de dominio existe.
    • Primero debe determinarse si pidiendo pre-confirmación de eliminación es de lógica de negocio o de vista de la lógica. Si es que la lógica de negocio, la DeleteFile método en el modelo no debe hacerlo, pero en lugar de devolver la pregunta de confirmación de objeto. Esto incluirá una referencia al delegado que tiene la eliminación. Si no es la lógica de negocio, la máquina virtual debe construir una máquina virtual de la cuestión en el DeleteFileCommand, con dos ICommand miembros. Uno para sí y uno no. Probablemente hay argumentos para ambos puntos de vista, y en la mayoría de RL de es probable que el uso de encuentro.

  10. 3

    ¿Por qué no se acaba de activar un evento en la VM y suscribirse al evento en la vista? Esto impediría la aplicación de la lógica y de la vista, por separado y todavía permite el uso de un niño de la ventana para los diálogos.

  11. 3

    He implementado un Comportamiento que escucha un Mensaje desde la Perspective. Se basa en Laurent Bugnion solución, pero ya que no utiliza código de atrás y es más reutilizable, creo que es más elegante.

    Cómo hacer WPF se comportan como si MVVM es apoyado fuera de la caja

    • Usted debe incluir el código completo aquí ya que es lo que tanto se requiere de buenas respuestas. Sin embargo, los enlaces de enfoque es bastante limpio, así que gracias por eso! 🙂
    • el código completo es muy largo, y es por eso que prefiero el enlace. He editado mi respuesta para reflejar los cambios y a punto de un vínculo que no se rompe
    • Gracias por la mejora. Sin embargo, es mejor proporcionar el código de 3 a página completa, se desplaza largo de aquí, de MODO que un enlace que puede estar desconectado algún día. Buenos artículos para temas complejos son siempre bastante largo y no veo ninguna ventaja en la apertura de una nueva pestaña, de conmutación y de desplazamiento que hay sobre el desplazamiento en la misma página/pestaña que yo era antes de que. 😉
  12. 2

    Creo que la vista podía tener el código para controlar el evento desde el punto de vista del modelo.

    Dependiendo el evento, con el escenario, que también podría tener un desencadenador de eventos que se suscribe a la vista de eventos del modelo, y una o más acciones para invocar en respuesta.

  13. 1

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

    Me gusta el enfoque de servicio – Es menos flexible para que los usuarios son menos propensos a romperse algo 🙂
    Es también coherente con la WinForms parte de mi aplicación (MessageBox.Mostrar)
    Pero si vas a mostrar un montón de diferentes cuadros de diálogo, luego InteractionRequest es una mejor manera de ir.

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

  14. 1

    Sé que es una pregunta vieja, pero cuando me hizo esta búsqueda, me parece un montón de pregunta relacionada, pero no encontré realmente una respuesta clara. Por eso hago mi propia implementación de un dialogbox/messagebox/popin, y se los comparto!

    Yo creo que es «MVVM prueba», y yo trato de hacer es sencillo y correcto, pero soy nuevo en WPF, así que siéntete libre de comentar, o incluso hacer pull request.

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

    Se puede utilizar como esto:

    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 como esta si quieres más sofisticados popin :

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

    Y se está mostrando cosas como esta :

    El manejo de los Diálogos en WPF con MVVM

  15. 1

    El enfoque estándar

    Después de pasar años lidiando con este problema en WPF, finalmente me di cuenta de la estándar forma de aplicación de los diálogos en WPF. Aquí están las ventajas de este enfoque:

    1. LIMPIO
    2. No viola el patrón de diseño MVVM
    3. ViewModal nunca se hace referencia de la interfaz de usuario de las bibliotecas (WindowBase, PresentationFramework etc.)
    4. Perfecto para la realización de pruebas automatizadas
    5. Diálogos pueden ser reemplazados fácilmente.

    Así que ¿cuál es la clave. Es DI + Coi.

    Aquí es cómo funciona. Estoy usando MVVM Light, pero este método puede ser extendido a otros marcos, así:

    1. Agregar un proyecto de Aplicación de WPF para su solución. Llaman Aplicación.
    2. Agregar un ViewModal la Biblioteca de clases de. Llaman VM.
    3. Aplicación referencias VM proyecto. VM proyecto no sabe nada acerca de la Aplicación.
    4. Agregar NuGet referencia a MVVM Light para ambos proyectos. Estoy usando MVVM Light Estándar estos días, pero usted está de acuerdo con el completo Framework versión demasiado.
    5. Agregar una interfaz IDialogService a VM proyecto:

      public interface IDialogService
      {
        void ShowMessage(string msg, bool isError);
        bool AskBooleanQuestion(string msg);
        string AskStringQuestion(string msg, string default_value);
      
        string ShowOpen(string filter, string initDir = "", string title = "");
        string ShowSave(string filter, string initDir = "", string title = "", string fileName = "");
        string ShowFolder(string initDir = "");
      
        bool ShowSettings();
      }
    6. Exponer a un público de la propiedad estática de IDialogService tipo en su ViewModelLocator, pero deje de registro de parte de la capa de la Vista a realizar. Esta es la clave.:

      public static IDialogService DialogService => SimpleIoc.Default.GetInstance<IDialogService>();
    7. Agregar una implementación de esta interfaz en el proyecto de la Aplicación.

      public class DialogPresenter : IDialogService
      {
          private static OpenFileDialog dlgOpen = new OpenFileDialog();
          private static SaveFileDialog dlgSave = new SaveFileDialog();
          private static FolderBrowserDialog dlgFolder = new FolderBrowserDialog();
      
          ///<summary>
          ///Displays a simple Information or Error message to the user.
          ///</summary>
          ///<param name="msg">String text that is to be displayed in the MessageBox</param>
          ///<param name="isError">If true, Error icon is displayed. If false, Information icon is displayed.</param>
          public void ShowMessage(string msg, bool isError)
          {
                  if(isError)
                          System.Windows.MessageBox.Show(msg, "Your Project Title", MessageBoxButton.OK, MessageBoxImage.Error);
                  else
                          System.Windows.MessageBox.Show(msg, "Your Project Title", MessageBoxButton.OK, MessageBoxImage.Information);
          }
      
          ///<summary>
          ///Displays a Yes/No MessageBox.Returns true if user clicks Yes, otherwise false.
          ///</summary>
          ///<param name="msg"></param>
          ///<returns></returns>
          public bool AskBooleanQuestion(string msg)
          {
                  var Result = System.Windows.MessageBox.Show(msg, "Your Project Title", MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes;
                  return Result;
          }
      
          ///<summary>
          ///Displays Save dialog. User can specify file filter, initial directory and dialog title. Returns full path of the selected file if
          ///user clicks Save button. Returns null if user clicks Cancel button.
          ///</summary>
          ///<param name="filter"></param>
          ///<param name="initDir"></param>
          ///<param name="title"></param>
          ///<param name="fileName"></param>
          ///<returns></returns>
          public string ShowSave(string filter, string initDir = "", string title = "", string fileName = "")
          {
                  if (!string.IsNullOrEmpty(title))
                          dlgSave.Title = title;
                  else
                          dlgSave.Title = "Save";
      
                  if (!string.IsNullOrEmpty(fileName))
                          dlgSave.FileName = fileName;
                  else
                          dlgSave.FileName = "";
      
                  dlgSave.Filter = filter;
                  if (!string.IsNullOrEmpty(initDir))
                          dlgSave.InitialDirectory = initDir;
      
                  if (dlgSave.ShowDialog() == DialogResult.OK)
                          return dlgSave.FileName;
                  else
                          return null;
          }
      
      
          public string ShowFolder(string initDir = "")
          {
                  if (!string.IsNullOrEmpty(initDir))
                          dlgFolder.SelectedPath = initDir;
      
                  if (dlgFolder.ShowDialog() == DialogResult.OK)
                          return dlgFolder.SelectedPath;
                  else
                          return null;
          }
      
      
          ///<summary>
          ///Displays Open dialog. User can specify file filter, initial directory and dialog title. Returns full path of the selected file if
          ///user clicks Open button. Returns null if user clicks Cancel button.
          ///</summary>
          ///<param name="filter"></param>
          ///<param name="initDir"></param>
          ///<param name="title"></param>
          ///<returns></returns>
          public string ShowOpen(string filter, string initDir = "", string title = "")
          {
                  if (!string.IsNullOrEmpty(title))
                          dlgOpen.Title = title;
                  else
                          dlgOpen.Title = "Open";
      
                  dlgOpen.Multiselect = false;
                  dlgOpen.Filter = filter;
                  if (!string.IsNullOrEmpty(initDir))
                          dlgOpen.InitialDirectory = initDir;
      
                  if (dlgOpen.ShowDialog() == DialogResult.OK)
                          return dlgOpen.FileName;
                  else
                          return null;
          }
      
          ///<summary>
          ///Shows Settings dialog.
          ///</summary>
          ///<returns>true if User clicks OK button, otherwise false.</returns>
          public bool ShowSettings()
          {
                  var w = new SettingsWindow();
                  MakeChild(w); //Show this dialog as child of Microsoft Word window.
                  var Result = w.ShowDialog().Value;
                  return Result;
          }
      
          ///<summary>
          ///Prompts user for a single value input. First parameter specifies the message to be displayed in the dialog 
          ///and the second string specifies the default value to be displayed in the input box.
          ///</summary>
          ///<param name="m"></param>
          public string AskStringQuestion(string msg, string default_value)
          {
                  string Result = null;
      
                  InputBox w = new InputBox();
                  MakeChild(w);
                  if (w.ShowDialog(msg, default_value).Value)
                          Result = w.Value;
      
                  return Result;
          }
      
          ///<summary>
          ///Sets Word window as parent of the specified window.
          ///</summary>
          ///<param name="w"></param>
          private static void MakeChild(System.Windows.Window w)
          {
                  IntPtr HWND = Process.GetCurrentProcess().MainWindowHandle;
                  var helper = new WindowInteropHelper(w) { Owner = HWND };
          }
      }
    8. Mientras que algunas de estas funciones son genéricas (ShowMessage, AskBooleanQuestion etc.), otros son específicos de este proyecto y el uso personalizado Windows. Usted puede agregar más personalizada de windows de la misma manera. La clave es mantener la interfaz de usuario elementos específicos en la capa de la Vista y acaba de exponer los datos devueltos utilizando POCOs en la VM de la capa de.
    9. Realizar Coi Registro de su interfaz en la capa de la Vista uso de esta clase. Usted puede hacer esto en su vista principal del constructor (después de InitializeComponent() llamada):

      SimpleIoc.Default.Register<IDialogService, DialogPresenter>();
    10. Hay que ir. Ahora usted tiene acceso a todos tus diálogo funcionalidad en tanto VM y visualización de capas. Su VM capa puede llamar a estas funciones como esto:

      var NoTrump = ViewModelLocator.DialogService.AskBooleanQuestion("Really stop the trade war???", "");
    11. Limpio de modo que usted puede ver. La máquina virtual de la capa no sabe nada acerca de cómo una pregunta de Sí/No será presentada al usuario por la capa de interfaz de usuario y todavía puede trabajar correctamente con el resultado devuelto desde el cuadro de diálogo.

    Otros beneficios libres de

    1. Por escrito de la prueba de unidad, puede proporcionar una implementación personalizada de IDialogService en su proyecto de Prueba y el registro de esa clase en la Coi en el constructor de tu clase de prueba.
    2. Tendrá que importar algunos espacios de nombres tales como Microsoft.Win32 para acceder Abrir y Guardar cuadros de diálogo. He dejado fuera porque también hay un WinForms versión de estos cuadros de diálogo disponibles, además de alguien que desee para crear su propia versión. También tenga en cuenta que algunas de las identificador utilizado en DialogPresenter son los nombres de mi propio windows (por ejemplo,SettingsWindow). Usted necesita para eliminar cualquiera de ellos a partir tanto de la interfaz y la implementación o proporcionar su propio windows.
    3. Si la VM realiza multi-threading, llame a MVVM Light DispatcherHelper.Initialize() temprano en la aplicación de su ciclo de vida.
    4. Excepto para DialogPresenter que se inyecta en la capa de la Vista, otros ViewModals debe ser registrado en ViewModelLocator y, a continuación, un estático público de la propiedad de ese tipo debe ser expuesta para que la capa de la Vista para consumir. Algo como esto:

      public static SettingsVM Settings => SimpleIoc.Default.GetInstance<SettingsVM>();
    5. Para la mayor parte, sus diálogos no debe tener ningún tipo de código subyacente para cosas como la unión o la configuración de DataContext etc. Usted no debería pasar cosas como los parámetros del constructor. XAML puede hacer todo por ti, como este:

      <Window x:Class="YourViewNamespace.SettingsWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:local="clr-namespace:YourViewProject"
        xmlns:vm="clr-namespace:YourVMProject;assembly=YourVMProject"
        DataContext="{x:Static vm:ViewModelLocator.Settings}"
        d:DataContext="{d:DesignInstance Type=vm:SettingsVM}" />
    6. Configuración DataContext esta manera le da todo tipo de tiempo de diseño de beneficios como Intellisense y la auto-realización.

    Esperanza de que ayuda a todos.

  16. 0

    Yo estaba pensando en un problema similar al preguntar cómo el modelo de vista de una tarea o de diálogo debe verse como.

    Mi solución actual se parece a esto:

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

    Cuando el modelo de vista decide que la entrada del usuario es necesario, se extrae una instancia de SelectionTaskModel con las opciones posibles para el usuario. La infraestructura de la que se ocupa de la educación de la vista correspondiente, que en el momento adecuado le llame la Choose() función con la elección del usuario.

  17. 0

    Luché con el mismo problema. He venido para arriba con una manera de intercommunicate entre la Vista y el ViewModel. Usted puede iniciar el envío de un mensaje desde la Perspective de la Vista para decirle a mostrar un cuadro de mensaje y se presentará un informe con el resultado. A continuación, el ViewModel puede responder a que el resultado devuelto por la Vista.

    Puedo demostrar esto en mi blog:

  18. 0

    He escrito bastante extenso artículo sobre este tema y también se desarrolló un pop-en la biblioteca para MVVM Diálogos. La adhesión estricta a MVVM es no sólo posible, sino muy limpio cuando se implementa correctamente, y puede ser extendido fácilmente a las bibliotecas de terceros que no se adhieren a ellos mismos:

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

  19. 0

    Lo siento, pero tengo que meter la cuchara. He sido a través de varias de las soluciones sugeridas, antes de encontrar el Prisma.Wpf.La interactividad del espacio de nombres en el proyecto Prism. Puede utilizar la interacción de las solicitudes y de la ventana emergente de acción para rodar una ventana personalizada o para necesidades más simples no están incorporados en la Notificación y Confirmación de ventanas emergentes. Estos crear una verdadera windows y son gestionados como tales. usted puede pasar un objeto de contexto con las dependencias que necesite en el cuadro de diálogo. Podemos utilizar esta solución en mi trabajo, ya lo he encontrado. Tenemos numerosos altos devs aquí y nadie ha llegado a algo mejor. Nuestra solución anterior fue el servicio de diálogo en una superposición y el uso de una clase de moderador para que esto suceda, pero había que tener las fábricas por todo el diálogo viewmodels, etc.

    Esto no es trivial, pero tampoco es super complicado. Y es construido en Prisma y por lo tanto es mejor (o la mejor) de la práctica en mi humilde opinión.

    Mis 2 centavos de dólar!

  20. -1

    EDIT: sí, estoy de acuerdo en que esto no es una forma correcta de MVVM enfoque y ahora estoy usando algo similar a lo sugerido por blindmeis.

    Uno de la manera que pudo para esto es

    En su Principal Modelo de Vista (donde se ha abierto el 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.
        }
    }

    Y en tu Ventana Modal Vista/Perspective:

    XAML:

    <Button Name="okButton" Command="{Binding OkCommand}" CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}">OK</Button>
    <Button Margin="2" VerticalAlignment="Center" Name="cancelButton" IsCancel="True">Cancel</Button>

    ViewModel:

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

    o similar a lo que está publicado aquí WPF, MVVM: Cómo cerrar una ventana

    • Yo no era el downvote, pero sospecho que es porque la vista-modelo tiene una referencia directa a la vista.
    • gracias por tu comentario. Estoy de acuerdo que esto no es un disociado de la solución. De hecho, no estoy usando algo similar a whar sugerido por blindmeis. Gracias de nuevo.
    • Es una mala forma de llegar a la vista cuando es tan fácil que no.

Kommentieren Sie den Artikel

Bitte geben Sie Ihren Kommentar ein!
Bitte geben Sie hier Ihren Namen ein