Buenos ejemplos de plantilla MVVM

141

Actualmente estoy trabajando con la plantilla MVVM de Microsoft y encuentro frustrante la falta de ejemplos detallados. El ejemplo de ContactBook incluido muestra muy poco manejo de comandos y el único otro ejemplo que he encontrado es el de un artículo de MSDN Magazine donde los conceptos son similares pero utilizan un enfoque ligeramente diferente y aún carecen de complejidad. ¿Hay ejemplos de MVVM decentes que al menos muestren operaciones CRUD básicas y cambio de diálogo / contenido?


Las sugerencias de todos fueron realmente útiles y comenzaré a compilar una lista de buenos recursos

Marcos / Plantillas

Artículos utiles

Screencasts

Bibliotecas adicionales

jwarzech
fuente
Me alegra que estos recursos hayan ayudado. Actualmente estoy en mi segunda aplicación MVVM de producción y continuaré agregando contenido que será útil para aquellos que comienzan cuando lo encuentro.
jwarzech

Respuestas:

59

Desafortunadamente, no hay una gran aplicación de ejemplo MVVM que haga todo, y hay muchos enfoques diferentes para hacer las cosas. Primero, es posible que desee familiarizarse con uno de los marcos de la aplicación (Prism es una opción decente), ya que le proporcionan herramientas convenientes como inyección de dependencia, comando, agregación de eventos, etc. para probar fácilmente diferentes patrones que le convengan .

El lanzamiento del prisma:
http://www.codeplex.com/CompositeWPF

Incluye una aplicación de ejemplo bastante decente (el operador de bolsa) junto con muchos ejemplos más pequeños y cómo hacerlo. Por lo menos, es una buena demostración de varios subpatrones comunes que las personas usan para hacer que MVVM realmente funcione. Tienen ejemplos para CRUD y diálogos, creo.

Prism no es necesariamente para cada proyecto, pero es bueno familiarizarse con él.

CRUD: esta parte es bastante fácil, los enlaces bidireccionales de WPF hacen que sea muy fácil editar la mayoría de los datos. El verdadero truco es proporcionar un modelo que facilite la configuración de la interfaz de usuario. Como mínimo, desea asegurarse de que su ViewModel (u objeto de negocio) se implemente INotifyPropertyChangedpara admitir el enlace y puede vincular propiedades directamente a los controles de la interfaz de usuario, pero también puede implementarlo IDataErrorInfopara la validación. Por lo general, si usa algún tipo de solución ORM, configurar CRUD es muy fácil.

Este artículo muestra operaciones simples de crud: http://dotnetslackers.com/articles/wpf/WPFDataBindingWithLINQ.aspx

Está construido en LinqToSql, pero eso es irrelevante para el ejemplo; todo lo que es importante es que sus objetos de negocio implementen INotifyPropertyChanged(qué clases generadas por LinqToSql sí). MVVM no es el punto de ese ejemplo, pero no creo que importe en este caso.

Este artículo demuestra la validación de datos
http://blogs.msdn.com/wpfsdk/archive/2007/10/02/data-validation-in-3-5.aspx

Una vez más, la mayoría de las soluciones ORM generan clases que ya implementan IDataErrorInfoy generalmente proporcionan un mecanismo para facilitar la adición de reglas de validación personalizadas.

La mayoría de las veces puede tomar un objeto (modelo) creado por algún ORM y envolverlo en un ViewModel que lo contiene y los comandos para guardar / eliminar, y está listo para vincular la IU directamente a las propiedades del modelo.

La vista se vería así (ViewModel tiene una propiedad Itemque contiene el modelo, como una clase creada en el ORM):

<StackPanel>
   <StackPanel DataContext=Item>
      <TextBox Text="{Binding FirstName, Mode=TwoWay, ValidatesOnDataErrors=True}" />
      <TextBox Text="{Binding LastName, Mode=TwoWay, ValidatesOnDataErrors=True}" />
   </StackPanel>
   <Button Command="{Binding SaveCommand}" />
   <Button Command="{Binding CancelCommand}" />
</StackPanel>

Diálogos: los diálogos y MVVM son un poco complicados. Prefiero usar una idea del enfoque de Mediador con diálogos, puede leer un poco más sobre esto en esta pregunta de StackOverflow:
ejemplo de diálogo WPF MVVM

Mi enfoque habitual, que no es MVVM clásico, se puede resumir de la siguiente manera:

Una clase base para un ViewModel de diálogo que expone comandos para las acciones de confirmación y cancelación, un evento que le permite a la vista saber que un diálogo está listo para cerrarse, y cualquier otra cosa que necesite en todos sus diálogos.

Una vista genérica para su diálogo: puede ser una ventana o un control de tipo de superposición "modal" personalizada. En esencia, es un presentador de contenido en el que volcamos el modelo de vista y maneja el cableado para cerrar la ventana; por ejemplo, en el cambio de contexto de datos, puede verificar si el nuevo ViewModel se hereda de su clase base, y si es así, suscríbase al evento de cierre relevante (el controlador asignará el resultado del diálogo). Si proporciona una funcionalidad de cierre universal alternativa (el botón X, por ejemplo), también debe asegurarse de ejecutar el comando de cierre correspondiente en ViewModel.

En algún lugar donde necesite proporcionar plantillas de datos para sus ViewModels, pueden ser muy simples, especialmente porque probablemente tenga una vista para cada cuadro de diálogo encapsulado en un control separado. La plantilla de datos predeterminada para un ViewModel se vería así:

<DataTemplate DataType="{x:Type vmodels:AddressEditViewModel}">
   <views:AddressEditView DataContext="{Binding}" />
</DataTemplate>

La vista de diálogo debe tener acceso a estos, porque de lo contrario no sabrá cómo mostrar el ViewModel, aparte de la interfaz de usuario del diálogo compartido, su contenido es básicamente el siguiente:

<ContentControl Content="{Binding}" />

La plantilla de datos implícitos asignará la vista al modelo, pero ¿quién la inicia?

Esta es la parte no tan mvvm. Una forma de hacerlo es usar un evento global. Lo que creo que es mejor hacer es usar una configuración de tipo de agregador de eventos, proporcionada mediante inyección de dependencia, de esta manera el evento es global para un contenedor, no para toda la aplicación. Prism utiliza el marco de la unidad para la semántica de contenedores y la inyección de dependencia, y en general me gusta bastante Unity.

Por lo general, tiene sentido que la ventana raíz se suscriba a este evento: puede abrir el cuadro de diálogo y establecer su contexto de datos en ViewModel que se pasa con un evento generado.

Configurar esto de esta manera permite que ViewModels solicite a la aplicación que abra un cuadro de diálogo y responda a las acciones del usuario allí sin saber nada acerca de la interfaz de usuario, por lo que la mayor parte de la MVVM-ness permanece completa.

Sin embargo, hay momentos en que la interfaz de usuario tiene que abrir los diálogos, lo que puede hacer las cosas un poco más complicadas. Considere, por ejemplo, si la posición del diálogo depende de la ubicación del botón que lo abre. En este caso, debe tener información específica de la IU cuando solicite que se abra un cuadro de diálogo. Por lo general, creo una clase separada que contiene un ViewModel y alguna información relevante de la interfaz de usuario. Desafortunadamente, algunos acoplamientos parecen inevitables allí.

Seudocódigo de un controlador de botones que genera un diálogo que necesita datos de posición del elemento:

ButtonClickHandler(sender, args){
    var vm = DataContext as ISomeDialogProvider; // check for null
    var ui_vm = new ViewModelContainer();
    // assign margin, width, or anything else that your custom dialog might require
    ...
    ui_vm.ViewModel = vm.SomeDialogViewModel; // or .GetSomeDialogViewModel()
    // raise the dialog show event
}

La vista de diálogo se unirá a los datos de posición y pasará el ViewModel contenido al interior ContentControl. ViewModel en sí todavía no sabe nada sobre la interfaz de usuario.

En general, no uso la DialogResultpropiedad return del ShowDialog()método ni espero que el hilo se bloquee hasta que se cierre el cuadro de diálogo. Un cuadro de diálogo modal no estándar no siempre funciona así, y en un entorno compuesto a menudo no desea que un controlador de eventos bloquee así de todos modos. Prefiero dejar que los ViewModels se ocupen de esto: el creador de un ViewModel puede suscribirse a sus eventos relevantes, establecer métodos de confirmación / cancelación, etc., por lo que no es necesario confiar en este mecanismo de interfaz de usuario.

Entonces, en lugar de este flujo:

// in code behind
var result = somedialog.ShowDialog();
if (result == ...

Yo suelo:

// in view model
var vm = new SomeDialogViewModel(); // child view model
vm.CommitAction = delegate { this.DoSomething(vm); } // what happens on commit 
vm.CancelAction = delegate { this.DoNothing(vm); } // what happens on cancel/close (optional)
// raise dialog request event on the container

Lo prefiero de esta manera porque la mayoría de mis cuadros de diálogo son controles pseudo-modales que no bloquean y hacerlo de esta manera parece más sencillo que evitarlo. Prueba fácil de unidad también.

Egor
fuente
Gracias por la respuesta detallada! Recientemente descubrí que mi mayor problema es cuando necesito que un MainViewModel se comunique con otros modelos de vista para manejar el flujo de la aplicación. Sin embargo, parece que MVVM + Mediator parece ser el enfoque popular.
jwarzech
2
El Mediador definitivamente ayuda, el patrón de agregación de eventos (Prism tiene una buena implementación) también es realmente útil cuando el objetivo es un bajo acoplamiento. Además, su modelo de vista principal generalmente tiene modelos de vista secundarios propios y no debería tener problemas para comunicarse con ellos. Debe usar un mediador o un agregador de eventos cuando los modelos de vista de su hijo necesitan interactuar con otros módulos de su aplicación que no necesariamente conocen, incluida la interfaz de usuario (mi ejemplo de diálogo es sobre este caso en particular).
Egor
1
Las pautas para trabajar con diálogos y ventanas han sido realmente útiles. Sin embargo, tengo algunos problemas: 1. ¿Cómo se configura el título de la ventana desde la vista? 2. ¿Cómo se ocupa de configurar la ventana del propietario?
djskinner
@Daniel Skinner: Asumo que estás hablando de diálogos aquí, corrígeme si me equivoco. El título del cuadro de diálogo es solo otra propiedad y puede vincularlo a lo que desee. Si siguió mi enfoque con una clase de modelo de vista de diálogo base (supongamos que tiene una propiedad de título), entonces en su ventana de diálogo genérica puede usar el enlace UI a UI para establecer el título en {Binding Path = DataContext.Title, ElementName = NameOfContentPresenter}. La ventana del propietario es un pequeño truco: significa que el mediador que realmente abre el cuadro de diálogo necesita saber acerca de la vista de la aplicación raíz.
Egor
De hecho, lo retiro, independientemente de cómo se estructura esto en algún momento, quienquiera que esté apareciendo el cuadro de diálogo debe tener una referencia a la ventana / vista de la aplicación raíz. Observe dónde dije: "Por lo general, tiene sentido que la ventana raíz se suscriba a este evento; puede abrir el cuadro de diálogo y establecer su contexto de datos en el modelo de vista que se pasa con un evento elevado". Aquí es donde establecerías el propietario.
Egor
6

Jason Dolinger hizo un buen screencast de MVVM. Como Egor mencionó, no hay un buen ejemplo. Están por todas partes. La mayoría son buenos ejemplos de MVVM, pero no cuando tienes problemas complejos. Todos tienen su propio camino. Laurent Bugnion también tiene una buena forma de comunicarse entre modelos de vista. http://blog.galasoft.ch/archive/2009/09/27/mvvm-light-toolkit-messenger-v2-beta.aspx Cinch también es un buen ejemplo. Paul Stovel tiene una buena publicación que explica mucho también con su marco Magellan.

nportelli
fuente
3

¿Has mirado a Caliburn ? La muestra de ContactManager contiene muchas cosas buenas. Los ejemplos genéricos de WPF también proporcionan una buena visión general de los comandos. La documentación es bastante buena y los foros están activos. ¡Recomendado!

Andy S
fuente
2

También compartí tu frustración. Estoy escribiendo una solicitud y tenía estos 3 requisitos:

  • Extensible
  • WPF con MVVM
  • Ejemplos compatibles con GPL

Todo lo que encontré fueron pedazos, así que comencé a escribirlo lo mejor que pude. Después de analizarlo un poco, me di cuenta de que podría haber otras personas (como usted) que podrían usar una aplicación de referencia, así que refactoricé el material genérico en un marco de aplicación WPF / MVVM y lo lancé bajo la LGPL. Lo llamé SoapBox Core . Si va a la página de descargas, verá que viene con una pequeña aplicación de demostración, y el código fuente de esa aplicación de demostración también está disponible para descargar. Espero que lo encuentres útil. Además, envíeme un correo electrónico a scott {at} soapboxautomation.com si desea obtener más información.

EDITAR : También publicó un artículo de CodeProject que explica cómo funciona.

Scott Whitlock
fuente
2

He escrito un ejemplo simple de MVVM desde cero en el proyecto de código, aquí está el enlace MVVM WPF paso a paso . Comienza desde una arquitectura simple de 3 capas y lo gradúa para usar un marco como PRISM.

ingrese la descripción de la imagen aquí

Shivprasad Koirala
fuente
1

Incluso compartí la frustración hasta que tomé el asunto en mis manos. Empecé IncEditor.

IncEditor ( http://inceditor.codeplex.com ) es un editor que intenta presentar a los desarrolladores WPF, MVVM y MEF. Lo comencé y logré obtener algunas funcionalidades como soporte de 'tema'. No soy un experto en WPF o MVVM o MEF, así que no puedo ponerle mucha funcionalidad. Les pido sinceramente que mejoren para que los locos como yo puedan entenderlo mejor.

Abdulsattar Mohammed
fuente