tutorial español wpf design mvvm architecture

wpf - español - Modelos gordos, ViewModels delgados y Vistas tontas, ¿el mejor enfoque MVVM?



mvvm wpf (2)

A través de la ayuda generosa en esta pregunta , armé la siguiente estructura de MVVM que muestra los cambios de un modelo en tiempo real en XAML (fecha / hora actual), muy agradable.

Una buena ventaja de esta configuración es que cuando observa su vista en el modo de diseño de Visual Studio o Blend, ve el tiempo pasar , lo que significa que en el momento del diseño tiene acceso a datos en tiempo real de su modelo.

En el proceso de hacer que esto funcione, me sorprendió ver la mayor parte del cambio masivo de mi ViewModel en mi modelo , incluida la implementación de INotifyPropertyChange. Otro cambio es que ya no estoy vinculado a las propiedades en ViewModel, sino a los métodos .

Por lo tanto, actualmente este es mi sabor favorito de MVVM:

  1. Ver es tonto

    • un ObjectDataProvider para cada objeto que necesita de su modelo
    • cada ObjectDataProvider se asigna a un método en ViewModel (no una propiedad)
    • no x: Propiedades del nombre en elementos XAML
  2. ViewModel es delgado

    • lo único en su ViewModel son los métodos a los que su vista se une
  3. El modelo es gordo

    • el modelo implementa INotifyPropertyChanged en cada una de sus propiedades.
    • para cada método en su ViewModel (por ejemplo, GetCurrentCustomer) hay un método singleton correspondiente en su modelo (por ejemplo, GetCurrentCustomer).
    • el modelo se ocupa de cualquier funcionalidad de enhebrado en tiempo real como en este ejemplo

Preguntas:

  1. Aquellos de ustedes que han estado implementando MVVM en escenarios reales, ¿es esta la estructura básica en la que también se han establecido y, de no ser así, cómo varía la suya?
  2. ¿Cómo ampliaría esto para incluir comandos enrutados y eventos enrutados?

El siguiente código funcionará si solo copia el código XAML y el código en un nuevo proyecto WPF.

XAML:

<Window x:Class="TestBinding99382.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:TestBinding99382" Title="Window1" Height="300" Width="300"> <Window.Resources> <ObjectDataProvider x:Key="DataSourceCustomer" ObjectType="{x:Type local:ShowCustomerViewModel}" MethodName="GetCurrentCustomer"/> </Window.Resources> <DockPanel DataContext="{StaticResource DataSourceCustomer}"> <StackPanel DockPanel.Dock="Top" Orientation="Horizontal"> <TextBlock Text="{Binding Path=FirstName}"/> <TextBlock Text=" "/> <TextBlock Text="{Binding Path=LastName}"/> </StackPanel> <StackPanel DockPanel.Dock="Top" Orientation="Horizontal"> <TextBlock Text="{Binding Path=TimeOfMostRecentActivity}"/> </StackPanel> </DockPanel> </Window>

Código detrás:

using System.Windows; using System.ComponentModel; using System; using System.Threading; namespace TestBinding99382 { public partial class Window1 : Window { public Window1() { InitializeComponent(); } } //view model public class ShowCustomerViewModel { public Customer GetCurrentCustomer() { return Customer.GetCurrentCustomer(); } } //model public class Customer : INotifyPropertyChanged { private string _firstName; private string _lastName; private DateTime _timeOfMostRecentActivity; private static Customer _currentCustomer; private Timer _timer; public string FirstName { get { return _firstName; } set { _firstName = value; this.RaisePropertyChanged("FirstName"); } } public string LastName { get { return _lastName; } set { _lastName = value; this.RaisePropertyChanged("LastName"); } } public DateTime TimeOfMostRecentActivity { get { return _timeOfMostRecentActivity; } set { _timeOfMostRecentActivity = value; this.RaisePropertyChanged("TimeOfMostRecentActivity"); } } public Customer() { _timer = new Timer(UpdateDateTime, null, 0, 1000); } private void UpdateDateTime(object state) { TimeOfMostRecentActivity = DateTime.Now; } public static Customer GetCurrentCustomer() { if (_currentCustomer == null) { _currentCustomer = new Customer { FirstName = "Jim" , LastName = "Smith" , TimeOfMostRecentActivity = DateTime.Now }; } return _currentCustomer; } //INotifyPropertyChanged implementation public event PropertyChangedEventHandler PropertyChanged; private void RaisePropertyChanged(string property) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(property)); } } } }


Aquí está mi opinión, por lo que vale:

Realmente no estoy de acuerdo con el enfoque que sugieres (a excepción de la vista tonta). En la vida real, a menudo tendrá que usar un modelo existente: podría ser un código heredado que no tiene el tiempo (o voluntad) de cambiar, o incluso una biblioteca para la que no tiene el código. En mi opinión, el modelo debe ignorar por completo la forma en que se mostrará, y debe ser fácilmente utilizable en una aplicación que no sea de WPF. Por lo tanto, no tiene que implementar ninguna interfaz específica como INotifyPropertyChanged de INotifyCollectionChanged para que pueda utilizarse en MVVM. Creo que toda la lógica relacionada con la IU debe residir en ViewModel.

En cuanto a RoutedEvents y RoutedCommands , no son realmente adecuados para su uso con el patrón MVVM. Por lo general, trato de usar el menor número de RoutedEvents posibles como RoutedEvents , y sin RoutedCommands en absoluto. En cambio, mis ViewModels exponen RelayCommand propiedades de RelayCommand que RelayCommand a la UI en XAML (consulte este artículo de Josh Smith para obtener detalles sobre RelayCommand ). Cuando realmente necesito manejar eventos para cierto control, utilizo comportamientos adjuntos para mapear los eventos a los comandos de ViewModel (eche un vistazo a la implementación de Marlon Grech )

Entonces, en resumen:

  • Dumb View
  • Gran e inteligente ViewModel
  • Cualquier modelo que quieras o tengas que usar

Por supuesto, es solo mi enfoque, y puede que no sea el mejor, pero me siento bastante cómodo con él;)


Estoy de acuerdo con Thomas. Mi consejo para cualquier persona en la arquitectura de WPF sería:

  • Las entidades simples de POCO sin INotifyPropertyChange, estado de seguimiento, BL, etc.
  • Modelos simples y pequeños de ViewModels que notifican vistas just-in-time
  • IU reutilizable simple con un sistema de navegación inteligente que evita jerarquías complejas de datos y complejos modelos de vista subyacentes
  • MVVM con vista Primer enfoque para mantener las dependencias simples
  • Operaciones asincrónicas con tareas o Rx
  • Un tema simple
  • No hay una interfaz de usuario robusta y compleja, simplemente, aproveche la composición de la interfaz de usuario de WPF y las capacidades de enlace
  • No dude en usar código subyacente para generar contenido dinámicamente (formularios, listas, etc.) y ahorre tiempo significativo en la configuración declarativa del ojo (se aplica a la mayoría de los casos) y para mí es obligatorio en 2015. Use métodos de extensión para crear una API Fluent para eso.