¿Qué es MVVM?

Completado

Las aplicaciones de .NET MAUI que no usan MVVM suelen tener más código en sus archivos de código subyacente. Los archivos de código subyacente en .NET MAUI siguen este patrón: {algo}.xaml.cs. Normalmente, la mayoría del código del archivo de código subyacente controla el comportamiento de la interfaz de usuario. El comportamiento de la interfaz de usuario puede incluir cualquier cosa que le suceda a la interfaz de usuario, como un cambio en el color o en el texto. Además, puede incluir cualquier cosa que suceda debido a la interfaz de usuario, incluidos los controladores de clic de botones.

Un problema de este enfoque es que resulta complicado escribir pruebas unitarias para archivos de código subyacente. Los archivos de código subyacente a menudo asumen un estado de la aplicación que se crea mediante el análisis de XAML o incluso mediante otras páginas. Estas condiciones son difíciles de controlar en un ejecutor de pruebas unitarias que incluso podría no ejecutarse en un dispositivo móvil, y mucho menos con una interfaz de usuario. Por lo tanto, las pruebas unitarias rara vez pueden probar los comportamientos de la interfaz de usuario en estos archivos.

Pero aquí es donde el patrón MVVM resulta útil. Cuando se usa correctamente, el patrón MVVM soluciona estos problemas moviendo la mayor parte de la lógica de comportamiento de la interfaz de usuario a clases que se pueden someter a pruebas unitarias, llamadas modelos de vista. El modelo MVVM suele usarse con marcos que admiten el enlace de datos. Eso se debe a que, con .NET MAUI, puede enlazar a datos cada elemento de la interfaz de usuario a una instancia de viewmodel y eliminar por completo, o casi, el código de una vista o el código subyacente.

¿Cuáles son los elementos de una aplicación MVVM?

Aunque viewmodel es el único elemento del patrón MVVM, este también define un elemento de modelo y un elemento de vista. Las definiciones de estos elementos son coherentes con otros patrones comunes, como el Modelo-Vista-Controlador (MVC).

¿Qué es un modelo?

En una aplicación MVVM, el término modelo se usa para indicar los datos y las operaciones empresariales. El modelo no se involucra con la presentación del usuario de la aplicación.

Una regla útil para determinar qué código pertenece al modelo es que debe ser portátil en distintas plataformas. Desde una aplicación móvil a una interfaz web o incluso un programa de línea de comandos, con el mismo modelo en todas las instancias. No está relacionado con cómo se muestra la información al usuario.

En lo que respecta a la aplicación de RR. HH. de nuestro escenario, el modelo podría incluir una clase Employee y una clase Department que contengan los datos y la lógica de esas entidades. El modelo también puede incluir elementos como una clase EmployeeRepository que contenga la lógica de persistencia. Otros modelos de diseño de software podrían considerar cosas como repositorios como independientes del modelo. Pero en el contexto de MVVM, se suele hacer referencia a cualquier lógica de negocios o datos empresariales como parte del modelo.

Aquí tiene dos ejemplos de un modelo en C#:

public class Employee
{
    public int Id { get; }
    public string Name { get; set; }
    public Employee Supervisor { get; set; }
    public DateTime HireDate { get; set; }
    public void ClockIn() { ... }
}

public class EmployeeRepository
{
    public IList<Employee> QueryEmployees() { ... }
    ...
}

¿Qué es una vista?

La vista de código controla las cosas que interactúan directamente con el usuario, como controles como botones y campos de entrada, así como otros elementos puramente visuales, como temas, estilos y fuentes.

En .NET MAUI, no es necesario escribir ningún código de C# para generar una vista usted mismo. En su lugar, a menudo define sus vistas por archivos XAML. Por supuesto, hay situaciones que llaman a un control de usuario personalizado, en el que se crea su propia vista a través del código.

¿Qué es una instancia de viewmodel?

Esto le lleva de nuevo a viewmodel. viewmodel es el intermediario entre la lógica de negocios (el modelo) y las vistas (la interfaz de usuario).

Diagrama que muestra cómo un modelo de vista es un intermediario entre un modelo y una vista.

Piense en lo que una instancia de viewmodel podría hacer para la aplicación de RR. HH. Supongamos que hay una vista que muestra el tiempo de vacaciones disponible de un empleado y desea que la cantidad de vacaciones se muestre como "2 semanas, 3 días, 4 horas". Pero la lógica de negocios del modelo proporciona ese mismo valor que 13,5 días, un número decimal que representa los días totales en un día laboral de 8 horas. El modelo de objetos podría ser similar a la siguiente lista:

  • Modelo: la clase Employee, que incluye un método:

    public decimal GetVacationBalanceInDays()
    {
        //Some math that calculates current vacation balance
        ...
    }
    
  • Modelo de vista: una clase EmployeeViewModel que tiene una propiedad similar a esta:

    public class EmployeeViewModel
    {
        private Employee _model;
    
        public string FormattedVacationBalance
        {
            get
            {
                decimal vacationBalance = _model.GetVacationBalanceInDays();
                ... // Some logic to format and return the string as "X weeks, Y days, Z hours"
            }
        }
    }
    
  • Vista: una página XAML que contiene una sola etiqueta y un botón cerrar. La etiqueta tiene un enlace a la propiedad de la instancia de viewmodel:

    <Label Text="{Binding FormattedVacationBalance}" />
    

    Solo necesitaríamos que el elemento BindingContext para la página estuviera establecido en una instancia de EmployeeViewModel.

En este ejemplo, nuestro modelo contiene la lógica de negocios. Esta lógica no está enlazada a una pantalla o dispositivo visual. Puede usar la misma lógica para un dispositivo portátil o un equipo de escritorio. La vista no sabe nada sobre la lógica de negocios. Los controles de vista, como la etiqueta, saben cómo obtener texto en la pantalla, pero no importa si es un número de vacaciones o una cadena aleatoria. La instancia de viewmodel tiene un conocimiento limitado de ambos mundos, de modo que puede actuar como un intermediario.

Lo interesante es ver cómo la instancia de viewmodel logra ser un intermediario: Expone propiedades a las que se puede enlazar una vista. Las propiedades públicas son la única manera en la que una instancia de viewmodel proporciona los datos. Esto le lleva a por qué se llama viewmodel. El "modelo" de MVVM representa la estructura, los datos y la lógica de los procesos empresariales, viewmodel representa la estructura, los datos y la lógica que necesita la vista.

¿Cómo funciona la vista con viewmodel?

Al examinar la relación que tiene la instancia de viewmodel con el modelo, se trata de una relación estándar de clase a clase. viewmodel tiene una instancia del modelo y expone aspectos del modelo a la vista mediante propiedades. Pero, ¿cómo obtiene y establece una vista las propiedades de viewmodel? Cuando la instancia de viewmodel cambia, ¿cómo actualiza la vista los controles visuales con nuevos valores? La respuesta es el enlace de datos.

Las propiedades de viewmodel se leen en el momento en que el objeto se enlaza a la vista. Pero el enlace no tiene forma de saber si las propiedades de viewmodel cambian después de aplicar el enlace a la vista. El cambio de viewmodel no propaga automáticamente el nuevo valor mediante el enlace a la vista. Para actualizar desde viewmodel a la vista, viewmodel debe implementar la interfaz System.ComponentModel.INotifyPropertyChanged.

La interfaz INotifyPropertyChanged declara un único evento denominado PropertyChanged. Toma un único parámetro, el nombre de la propiedad que cambió su valor. El sistema de enlace usado en .NET MAUI comprende esta interfaz y escucha ese evento. Cuando cambia una propiedad en viewmodel y genera el evento, el enlace notifica al destino del cambio.

Piense en cómo funciona en la aplicación de RR. HH. con una instancia de viewmodel de empleado. viewmodel tiene propiedades que representan a un empleado, como el nombre del empleado. viewmodel implementa la interfaz INotifyPropertyChanged y, cuando cambia la propiedad Name, genera el evento PropertyChanged, de la siguiente manera:

using System.ComponentModel;

public class EmployeeViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler? PropertyChanged;
    private Employee _model;

    public string Name
    {
        get {...}
        set
        {
            _model.Name = value;
            OnPropertyChanged(nameof(Name))
        }
    }

    protected void OnPropertyChanged(string propertyName) =>
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

La vista que describe los detalles del empleado contiene un control de etiqueta enlazado a la propiedad Name de viewmodel:

<Label Text="{Binding Name}" />

Cuando cambia la propiedad Name en viewmodel, se genera el evento PropertyChanged con el nombre de esa propiedad. El enlace escucha el evento y, a continuación, notifica a la etiqueta que cambió la propiedad Name. A continuación, la propiedad Text de la etiqueta se actualiza con el valor más reciente.

Comprobación de conocimientos

1.

Un desarrollador está trabajando en una aplicación .NET MAUI y quiere implementar el patrón MVVM. Han creado un modelo para las operaciones y los datos empresariales, y una vista para la interacción del usuario. ¿Qué deben crear después para actuar como intermediario entre el modelo y la vista?

2.

Un equipo está desarrollando una aplicación móvil mediante .NET MAUI y quiere asegurarse de que su lógica de negocios es portátil en distintas plataformas. ¿Dónde deben colocar esta lógica de negocios en el patrón MVVM?