2009-11-03 16 views
25

¿Alguien tiene algún ejemplo de mostrar un diálogo de ventana usando MVVM (Prism)? - por ejemplo, una ventana de configuración de configuración cuando se ejecuta un comando.Ejemplo de diálogo WPF MVVM

Todos los ejemplos que he visto utilizar el patrón de mediador que está muy bien, pero también todos tienen una referencia a la vista en el modelo de vista que no es ideal (estamos usando DataTemplates)

Gracias

Respuesta

12

La forma en que hago esto es usar también el patrón del mediador. Cuando ViewModel quiere mostrar un diálogo, envía un mensaje que es recogido por la ventana principal de la aplicación. El mensaje contiene una instancia del ViewModel utilizado por el cuadro de diálogo.

La ventana principal luego construye una instancia de la ventana de diálogo, le pasa el modelo de vista y muestra el cuadro de diálogo. El resultado del diálogo se transfiere a la persona que llama en el mensaje original.

se ve algo como esto:

En su modelo de vista:

DialogViewModel viewModel = new DialogViewModel(...); 
ShowDialogMessage message = new ShowDialogMessage(viewModel); 

_messenger.Broadcast(message); 

if (message.Result == true) 
{ 
    ... 
} 

En el código subyacente de la ventana principal:

void RecieveShowDialogMessage(ShowDialogMessage message) 
{ 
    DialogWindow w = new DialogWindow(); 
    w.DataContext = message.ViewModel; 
    message.Result = w.ShowDialog(); 
} 

espero que esto es suficiente para darle la idea. ..

+0

Gracias por la respuesta. ¿Podría aclarar el escenario a continuación, por favor? Supongamos que muestra un modelo de vista en el DialogWindow con los comandos Guardar y Cancelar. Cuando el usuario hace clic en Guardar en la vista (vinculado a SaveCommand), quizás desee ejecutar alguna validación y guardar la configuración, solo si el diálogo se cierra correctamente. Parece que no entiendo cómo la máquina virtual establecería el DialogResult de la vista (por lo tanto, cerrará el cuadro de diálogo). Gracias de nuevo. – Oll

+0

Hay muchas maneras de hacer que la VM interactúe con la vista sin romper la abstracción. Por ejemplo, la clase base del modelo de vista de diálogo tiene típicamente algunas propiedades y métodos básicos para hacer que mostrar diálogos sea un poco más fácil (normalmente tengo una interfaz para un modelo de vista de diálogo que incluye cosas como un método que se ejecuta en carga de vista, así como el resultado del diálogo, comandar/abortar comandos, etc.). Una manera fácil de obtener el modelo de vista para decirle a la vista que cierre es exponer una propiedad (CanClose, por ejemplo). – Egor

+0

Luego, cree una propiedad de dependencia correspondiente en su cuadro de diálogo, y cuando los cambios en el contexto de datos establezcan un enlace entre los dos, de esa forma puede manejar cualquier lógica en el controlador de eventos modificado de su propiedad encuadernada. Por otra parte, es incluso más fácil simplemente exponer un evento "cerrado" en su modelo de vista de diálogo, y en el contexto de datos de ventana el controlador modificado se suscribe a este evento, el manejador puede asignar el resultado de diálogo apropiado y cerrar la ventana. – Egor

0

Puede que le interese la siguiente aplicación de muestra:

http://compositeextensions.codeplex.com

Utiliza Prism2 con el PresentationModel (aka MVVM) patrón. La aplicación de muestra contiene un diálogo modal.

2

Como entendí su comentario anterior, la pregunta no es tanto sobre mostrar los diálogos como sobre ocultarlos. Hay dos maneras de resolver este problema:

  1. Use la ventana de diálogo estándar para implementar la vista. Esto requeriría tener una forma de comunicación débilmente acoplada entre View y ViewModel para que ViewModel pueda notificar a la Vista que está bien cerrar sin tener una referencia a una vista.

    Existen múltiples marcos que permitirían hacerlo: los agregadores de eventos de Prism serían uno de ellos. En este escenario, View se suscribiría a un evento (por ejemplo, MyDialogResultValidated), y al recibir el evento, establecería el DialogResult de forma accidental. ViewModel (en su SaveCommand) activará el evento si la validación fue exitosa.

  2. No utilice la ventana de diálogo estándar para implementar la vista. Esto requeriría tener una superposición que emularía efectivamente la modalidad.

    En este escenario, la Visibilidad de la Vista y de la superposición se unirá a la propiedad IsVisible de ViewModel que se establecería en consecuencia mediante la implementación de SaveCommand, o siempre que ViewModel necesite mostrar la Vista.

El primer enfoque sería necesario tener un poco de código en el código subyacente, requiere la adición de evento (s) global, y (posiblemente) es menos MVVM-ish.El segundo enfoque requeriría la implementación (o el uso de la implementación de otra persona) de la superposición, pero no requerirá tener código en código subyacente, no requerirá eventos globales, y es (discutible) más MVVM-ish .

21

Utilizaría un servicio para mostrar el cuadro de diálogo. El servicio también puede vincular vistas con viewmodels.

public interface IDialogService { 
    void RegisterView<TView, TViewModel>() where TViewModel:IDialogViewModel; 
    bool? ShowDialog(IDialogViewModel viewModel); 
} 

public interface IDialogViewModel { 
    bool CanClose(); 
    void Close(); 
} 


RegisterView sólo enlaces del tipo de vista con el tipo de modelo de vista. Puede configurar estos enlaces en la inicialización del módulo. Esto es más simple que tratar de obtener módulos para registrar plantillas de datos en la capa superior de su aplicación.

ShowDialog Muestra el ViewModel que desea visualizar. Devuelve true, false y null para cerrar al igual que el método Window.ShowDialog. La implementación solo crea una nueva vista de tipo TView desde su contenedor, lo conecta al ViewModel proporcionado y lo muestra.

IDialogViewModel proporciona un mecanismo para ViewModel para hacer la verificación y cancelar el cierre del cuadro de diálogo.

Tengo una ventana de diálogo estándar, con un control de contenido en ella. Cuando se llama al ShowDialog, crea un nuevo cuadro de diálogo estándar, agrega la vista al control de contenido, conecta el modelo de vista y lo muestra. El cuadro de diálogo estándar ya tiene los botones [Aceptar] y [Cancelar] con la lógica adecuada para llamar a los métodos correctos desde IDialogViewModel.

+0

¡Hola, Cameron! ¿Cómo informa la vista al servicio si se presionó Aceptar o Cancelar? –

+0

La forma estándar. La implementación del código de 'ShowDialog' solo encontraría la vista que necesita mostrar, y luego llamaría' ShowDialog' en esa vista, y pasaría el resultado. –

+0

¿Cómo los cambios de propiedad dentro de IDIalogViewModel activan la evaluación de CanExecute de los comandos vinculados a los botones [OK] y [Cancelar] en el diálogo estándar? InvalidateRequerySuggested? – jan

0

No es Prisma, pero este MVVM demo tiene un cuadro de diálogo de opciones que es completamente MVVM.

2

Estoy de acuerdo, que el uso del servicio para mostrar el diálogo según el patrón MVVM es la solución más simple. Pero, también me pregunté a mí mismo si hay 3 ensambles en mi modelo de proyecto, ViewModel, View y de acuerdo con el ensamblaje de patrones de MVVM ViewModel tiene una referencia a Model y View a Model y ViewModel, ¿dónde debería colocar la clase DialogService? Si coloco uno en el ensamblado de ViewModel, no tengo posibilidades de crear la instancia de DialogView; por otro lado, si ubicaré DialogService en el ensamblaje de Vista, ¿cómo debería inyectarlo en mi clase ViewModel?

lo tanto, me recoment vistazo a Advanced MVVM scenarios with PrismParte: Uso de solicitudes Interacción Objetos

Como ejemplo de este enfoque:

DialogViewModelBase

public abstract class DialogViewModelBase : ViewModelBase 
{ 
    private ICommand _ok; 

    public ICommand Ok 
    { 
     get { return _ok ?? (_ok = new DelegateCommand(OkExecute, CanOkExecute)); } 
    } 

    protected virtual bool CanOkExecute() 
    { 
     return true; 
    } 

    protected virtual void OkExecute() 
    { 
     _isSaved = true; 
     Close = true; 
    } 

    private ICommand _cancel; 

    public ICommand Cancel 
    { 
     get 
     { 
      return _cancel ?? (_cancel = new DelegateCommand(CancelExecute, CanCancelExecute)); 
     } 
    } 

    protected virtual bool CanCancelExecute() 
    { 
     return true; 
    } 

    protected virtual void CancelExecute() 
    { 
     Close = true; 
    } 

    private bool _isSaved = false; 
    public bool IsSaved 
    { 
     get { return _isSaved; } 
    } 

    private bool _close = false; 

    public bool Close 
    { 
     get { return _close; } 
     set 
     { 
      _close = value; 
      RaisePropertyChanged(() => Close); 
     } 
    } 
} 

CreateUserStoryViewModel:

public class CreateUserStoryViewModel : DialogViewModelBase 
{ 
    private string _name = String.Empty; 

    public string Name 
    { 
     get { return _name; } 
     set 
     { 
      _name = value; 
      RaisePropertyChanged(() => Name); 
     } 
    } 
} 

CreateUserStoryRequest

private InteractionRequest<Notification> _createUserStoryRequest; 
public InteractionRequest<Notification> CreateUserStoryRequest 
{ 
    get 
    { 
     return _createUserStoryRequest ?? (_createUserStoryRequest = new InteractionRequest<Notification>()); 
    } 
} 

Comando CreateUserStory

private void CreateUserStoryExecute() 
{ 
    CreateUserStoryRequest.Raise(new Notification() 
    { 
     Content = new CreateUserStoryViewModel(), 
     Title = "Create User Story" 
    }, 
    notification => 
       { 
         CreateUserStoryViewModel createUserStoryViewModel = 
           (CreateUserStoryViewModel)notification.Content; 
         if (createUserStoryViewModel.IsSaved) 
         { 
         _domainContext.CreateUserStory(
new UserStory(){ Name = createUserStoryViewModel.Name, }); 
         } 
       }); 
} 

XAML:

<!--where xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" 
      xmlns:ir="clr-namespace:Microsoft.Practices.Prism.Interactivity.InteractionRequest;assembly=Microsoft.Practices.Prism.Interactivity"--> 

<i:Interaction.Triggers> 
    <ir:InteractionRequestTrigger SourceObject="{Binding CreateUserStoryRequest}"> 
    <ir:PopupChildWindowAction> 
     <ir:PopupChildWindowAction.ChildWindow> 
     <view:CreateUserStory /> 
     </ir:PopupChildWindowAction.ChildWindow> 
    </ir:PopupChildWindowAction> 
    </ir:InteractionRequestTrigger> 
</i:Interaction.Triggers> 
+0

Vladimir: He intentado el enfoque, pero no funciona para mí. ¿Puede explicar qué es _domainContext? Mi escenario es abrir un diálogo modal que tiene un cuadro de texto, etiqueta y pdfviewer. Tengo que cambiar el contexto de datos de ModalDialog [es decir. pasando esos valores como un modelo de vista] de acuerdo con el botón (disparador), haga clic. Cualquier idea o referencia sería útil ... Gracias –

+0

_domainContext en mi ejemplo es una especie de repositorio. Es solo un ejemplo y puede reemplazarlo con una llamada de servicio WCF o guardarlo en el disco. Esta muestra funcionará para su escenario. Necesita reemplazar CreateUserStoryViewModel con su propio ModalDialogViewModel con propiedades Title, Text etc.y enlazar estas propiedades a la Etiqueta ModalDialogView y al PdfViewer (se debe usar ModalDialogView en lugar de mi vista CreateUserStory). –

+0

Esto no parece funcionar con WPF. El elemento no existe. Sin embargo, sí lo hace en Silverlight. –