2009-01-08 11 views
16

He estado desarrollando una aplicación LOB muy grande usando mi sabor de M-V-VM al que llamo M-V-MC (Model-View-ModelController), que es una especie de combinación entre M-V-C y M-V-VM. Publiqué this answer sobre cómo se instancian las vistas en M-V-VM a la pregunta "what-are-the-most-common-mistakes-made-in-wpf-development".M-V-VM: algún ejemplo de uso de comandos en ViewModel?

Sam hizo el siguiente comentario con respecto a mi respuesta:

Esto crea un seguimiento-pregunta: ¿Cómo se crea los puntos de vista? Yo uso RelayCommands para vincular acciones de la vista al ViewModel, por lo que la vista ni siquiera sabe que una acción tiene disparado, no sabe que debe abrir una nueva vista . Solución: cree un evento en la máquina virtual de la vista para suscribirse?

Cuando originalmente comenzó el desarrollo M-V-VM tenía esta idea de que todo debe vivir en el modelo de vista, y han estudiado un montón de ejemplos de tipos como Josh Smith y Karl Shifflett. Sin embargo, todavía tengo que encontrar un buen ejemplo de cuándo un comando necesita vivir en ViewModel.

Por ejemplo, supongamos que tengo un ListView que muestra Clientes, y un botón que hago clic para permitirme editar el cliente seleccionado actualmente. ListView (Ver) está vinculado a un clienteVM (ViewModel). Al hacer clic en el botón, se activa EditCustomerCommand, que abre una ventana emergente que me permite editar todas las propiedades de CustomerVM. ¿Dónde vive este EditCustomerCommand? Si se trata de abrir una ventana (funcionalidad UI), ¿no debería definirse en el código subyacente de la vista? alt text

¿Alguien tiene algún ejemplo de cuándo debería definir un comando en la Vista frente al Modelo de Vista?

Matthew Wright estados siguientes:

nuevo y borrar de una lista sería buenos ejemplos. En esos casos, se agrega un registro en blanco o el ViewModel borra el registro actual . Cualquier acción tomada por la vista debe estar en respuesta a esos eventos que ocurren.

Entonces, si hago clic en el botón nuevo, ¿qué ocurre? El Parent ViewModel crea una nueva instancia de CustomerVM y la agrega a su colección ¿no? Entonces, ¿cómo se abriría mi pantalla de edición? La vista debe crear una nueva instancia del Cliente ViewModel y pasarlo al método ParentVM.Add (newlyCreatedVM) ¿verdad?

Digamos que elimino un registro de cliente a través del DeleteCommand que vive en la máquina virtual. la VM llama a la capa empresarial e intenta eliminar el registro. No puede, por lo que devuelve un mensaje a la VM. Quiero mostrar este mensaje en el cuadro de diálogo. ¿Cómo obtiene la vista el mensaje de la acción de comando?

Respuesta

3

Nunca pensé que me vería citado en una pregunta.

I reflexionado sobre esta cuestión a mí mismo por algún tiempo, y tomó una decisión bastante pragmática para mi base de código:

En mi base de código, el modelo de vista está recibiendo llamadas cuando las acciones suceden, y quería que se mantenga de esta manera . Además, no quiero que ViewModel controle las vistas.

¿Qué hice?
he añadido un controlador para la navegación:

public interface INavigation 
{ 
    void NewContent(ViewModel viewmodel); 
    void NewWindow(ViewModel viewmodel); 
} 

Este controlador no contener dos acciones: NewContent() no mostrar el nuevo contenido en la ventana actual, NewWindow() crea una nueva ventana, lo llena con el contenido y lo muestra
Por supuesto, mis viewmodels no tienen idea de qué vista mostrar. Pero sí saben qué modelo de vista quieren mostrar, así que según su ejemplo cuando se ejecuta DeleteCommand, llamaría a la función de servicio de navegación NewWindow (new ValidateCustomerDeletedViewModel()) para mostrar una ventana que indique 'el cliente ha sido eliminado' (exceso de este simple cuadro de mensaje, pero sería fácil tener una función de navegador especial para cuadros de mensajes simples).

¿Cómo obtiene viewmodel el servicio de navegación?

Mi clase de modelo de vista tiene una propiedad para el control de navegación:

public class ViewModel 
{ 
    public INavigation Navigator { get; set; } 
    [...] 
} 

Cuando un modelo de vista está unido a una ventana (o lo que muestra la vista), la ventana se establezca la propiedad Navigator, por lo que el modelo de vista puede llamarlo

¿Cómo crea el navegador la vista del modelo de vista?

usted podría tener una lista simple que ver a crear para el cual modelo de vista, en mi caso se puede utilizar la reflexión sencilla ya que los nombres son coincidentes:

public static FrameworkElement CreateView(ViewModel viewmodel) 
{ 
    Type vmt = viewmodel.GetType(); 
    // big bad dirty hack to get the name of the view, but it works *cough* 
    Type vt = Type.GetType(vmt.AssemblyQualifiedName.Replace("ViewModel, ", "View, ")); 
    return (FrameworkElement)Activator.CreateInstance(vt, viewmodel); 
} 

Por supuesto la vista necesita un constructor de aceptar el modelo de vista como parámetro:

public partial class ValidateCustomerDeletedView : UserControl 
{ 
    public ValidateCustomerDeletedView(ValidateCustomerDeletedViewModel dac) 
    { 
    InitializeComponent(); 
    this.DataContext = dac; 
    } 
} 

¿Cómo se ve mi ventana?

Simple: mi ventana principal implementa la interfaz INavigation y muestra una página de inicio en la creación. Vea usted mismo:

public partial class MainWindow : Window, INavigation 
{ 
    public MainWindow() 
    { 
    InitializeComponent(); 
    NewContent(new StartPageViewModel()); 
    } 

    public MainWindow(ViewModel newcontrol) 
    { 
    InitializeComponent(); 
    NewContent(newcontrol); 
    } 

    #region INavigation Member 
    public void NewContent(ViewModel newviewmodel) 
    { 
    newviewmodel.Navigator = this; 
    FrameworkElement ui = App.CreateView(newviewmodel); 
    this.Content = ui; 
    this.DataContext = ui.DataContext; 
    } 

    public void NewWindow(ViewModel viewModel) 
    { 
    MainWindow newwindow = new MainWindow(viewModel); 
    newwindow.Show(); 
    } 
    #endregion 
} 

(Esto funciona igual de bien con un NavigationWindow y envolver la vista en una página)

Por supuesto, esto es comprobable, ya que el controlador de navegación puede ser burlado fácilmente.

No estoy muy seguro de si esta es una solución perfecta, pero funciona muy bien para mí en este momento. ¡Cualquier idea y comentario son bienvenidos!

0

Nuevo y eliminar de una lista serían buenos ejemplos. En esos casos, se agrega un registro en blanco o el ViewModel borra el registro actual. Cualquier acción tomada por la vista debe ser en respuesta a esos eventos que ocurren.

+0

Entonces, si hago clic en el botón nuevo, ¿qué ocurre? La vista debería crear una nueva instancia de ViewModel y pasarla a CurrentVM.Add (newlyCreatedVM), ¿verdad? – Micah

+0

Digamos que elimino un registro de cliente a través de DeleteCommand que vive en la máquina virtual. la VM llama a la capa empresarial e intenta eliminar el registro. No puede, por lo que devuelve un mensaje a la VM. Quiero mostrar este mensaje en el cuadro de diálogo. ¿Cómo obtiene la vista el mensaje de la acción de comando? – Micah

0

Una forma sería usar un objeto de parámetro de comando que la capa empresarial puede modificar y su VM puede procesar después de ejecutar el comando.

1

Para su caso de cuadro de mensaje de eliminación, abstraigo messageboxes a través de una interfaz. Similar a ésto. También inyecté estas interfaces para mi aplicación WPF.

Constructor

public MyViewModel(IMessage msg) 
    { 
     _msg = msg; 
    } 

Luego, en el método borrar método en el modelo de vista ... algo así como

public void Delete() 
    { 
     if(CanDelete) 
     { 
     //do the delete 
     } 
     else 
     { 
     _msg.Show("You can't delete this record"); 
     } 
    } 

Esto hará que sea comprobable, puede enchufar en un diferentes implementaciones IMessage que don en realidad, muestra un cuadro de mensaje. Esos podrían simplemente imprimir en la consola, para fines de prueba. Obviamente su aplicación WPF podría tener una aplicación como

public class MessageBoxQuestion : IMessage 
{ 
    public void Show(string message) 
    { 
    MessageBox.Show(message); 
    } 
} 

Hacer esto hace que las pruebas de las diferentes rutas (piensa Sí/No) diálogos muy fácil y sencillo. Puedes imaginar una confirmación de eliminación. Puede usar una instancia concreta de IMessage para devolver verdadero/falso para confirmación o simular el contenedor durante su prueba.

[Test] 
public void Can_Cancel_Delete() 
{ 
    var vm = new ProductViewModel(_cancel); 
    ... 

} 
[Test] 
public void Can_Confirm_Delete() 
{ 
    var vm = new ProductViewModel(_yes); 
    ... 

} 

Para su otra pregunta sobre cuándo utilizar el comando, instanciar las vistas Agregar nuevo o Detalles de la vista en cuestión. Tal como lo has hecho en tu ejemplo. Las vistas solo son instanciadas por otras Vistas en nuestra aplicación. No uso un comando en esos casos. Sin embargo, utilizo las propiedades de ViewModel de la vista primaria en la vista secundaria.

public void Object_DoubleClick(object sender, EventArgs e) 
{ 
    var detailView = new DetailView(ViewModel.Product); 
    detailView.Show(); 
} 

Hope this helps!

+0

En mi comprensión de MVV, "subir la llamada" de la máquina virtual a la V, incluso a través de una interfaz, no sirve. Acabo de establecer una propiedad (como ErrorText o IDataErrorInfo) y dejar que la V se ocupe de ella a través de PropertyChanged –

+0

David, ¿podría explicar por qué "subir" no es una buena idea? ¡Esto sería muy útil! – Sam

Cuestiones relacionadas