wpf - que - MV-VM: ¿algún ejemplo de uso de comandos en ViewModel?
wpf c# mvvm (4)
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.
He estado desarrollando una aplicación LOB muy grande usando mi experiencia de MV-VM que llamo MV-MC (Model-View-ModelController), que es una especie de combinación entre MVC y MV-VM. Publiqué esta respuesta sobre cómo se instancian las vistas en MV-VM a la pregunta " qué son los más comunes-errores-hechos-en-wpf-desarrollo ".
Sam hizo el siguiente comentario con respecto a mi respuesta:
Esto crea una pregunta de seguimiento: ¿cómo se crean las vistas? Yo uso RelayCommands para vincular acciones desde la vista al ViewModel, por lo que la vista ni siquiera sabe que una acción se ha disparado, no sabe que debe abrir una nueva vista. Solución: ¿crear un evento en la máquina virtual para que la vista se suscriba?
Cuando comencé originalmente el desarrollo MV-VM, tuve la idea de que TODO debería vivir en ViewModel, y he estudiado muchos ejemplos de chicos 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, digamos 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?
¿Alguien tiene algún ejemplo de cuándo debería definir un comando en la Vista versus el Modelo de Vista?
Matthew Wright declara a continuación:
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.
Entonces, si hago clic en el botón nuevo, ¿qué sucede? 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 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?
Para su caso de cuadro de mensaje de borrar, abstraigo los cuadros de mensaje 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 eliminar método en ViewModel ... algo así como
public void Delete()
{
if(CanDelete)
{
//do the delete
}
else
{
_msg.Show("You can''t delete this record");
}
}
Esto lo hará comprobable, puede conectar una implementación de IMessage diferente que en realidad no muestra un mensaje. Esos podrían simplemente imprimir en la consola, para fines de prueba. Obviamente, su aplicación WPF podría tener una implementación como
public class MessageBoxQuestion : IMessage
{
public void Show(string message)
{
MessageBox.Show(message);
}
}
Hacer esto hace que probar las diferentes rutas (pensar en diálogos Sí / No) sea muy fácil y directo. 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 usar 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();
}
¡Espero que esto ayude!
Una forma sería usar un objeto de parámetro de comando que la capa empresarial pueda modificar y su máquina virtual pueda procesar después de ejecutar el comando.
Nunca pensé que me vería citado en una pregunta.
Reflexioné sobre esta pregunta por mí mismo durante un tiempo e hice una decisión bastante pragmática para mi código base:
En mi base de código, se llama a ViewModel cuando ocurren las acciones, y quería que siguiera así. Además, no quiero que ViewModel controle las vistas.
¿Qué hice?
Agregué un controlador para la navegación:
public interface INavigation
{
void NewContent(ViewModel viewmodel);
void NewWindow(ViewModel viewmodel);
}
Este controlador contiene dos acciones: NewContent () muestra nuevo contenido en la ventana actual, NewWindow () crea una nueva ventana, la rellena con el contenido y la muestra.
Por supuesto, mis viewmodels no tienen idea de qué ver para mostrar. Pero sí saben qué modelo de vista quieren mostrar, así que de acuerdo con su ejemplo cuando se ejecuta DeleteCommand, llamaría a la función de servicio de navegación NewWindow (nuevo ValidateCustomerDeletedViewModel ()) para mostrar una ventana que indica ''el cliente ha sido eliminado'' (overkill for este simple messagebox, pero sería fácil tener una función de navegador especial para messageboxes simples).
¿Cómo obtiene viewmodel el servicio de navegación?
Mi clase viewmodel tiene una propiedad para el controlador de navegación:
public class ViewModel
{
public INavigation Navigator { get; set; }
[...]
}
Cuando un modelo de vista se adjunta a una ventana (o lo que sea que muestre la vista), la ventana establecerá la propiedad del Navegador, por lo que el modelo de vista puede invocarla.
¿Cómo crea el navegador la vista del modelo de vista?
Podrías tener una lista simple que ver para crear para qué modelo de vista, en mi caso puedo usar la reflexión simple ya que los nombres coinciden:
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 que acepte 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. Ver por ti 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 una ventana de navegación y envolviendo la vista en una página)
Por supuesto, esto es comprobable, ya que el controlador de navegación se puede burlar fácilmente.
No estoy seguro si esta es una solución perfecta, pero funciona bien para mí en este momento. ¡Cualquier idea y comentario son bienvenidos!