Primeros pasos con mvvm

C# Resumen de MVVM y ejemplo completo

Resumen:

MVVM es un patrón arquitectónico que está representado por tres componentes distintos, Modelo, Vista y ViewModel. Para comprender estas tres capas, es necesario definir brevemente cada una, seguida de una explicación de cómo funcionan juntas.

Modelo es la capa que impulsa la lógica empresarial. Recupera y almacena información de cualquier fuente de datos para el consumo de ViewModel.

ViewModel es la capa que actúa como puente entre la Vista y el Modelo. Puede o no transformar los datos sin procesar del Modelo en una forma presentable para la Vista. Un ejemplo de transformación sería: una bandera booleana del modelo a una cadena de ‘Verdadero’ o ‘Falso’ para la vista.

Ver es la capa que representa la interfaz del software (es decir, la GUI). Su función es mostrar la información desde el ViewModel al usuario y comunicar los cambios de la información al ViewModel.

Estos tres componentes funcionan juntos haciendo referencia entre sí de la siguiente manera:

  • La Vista hace referencia al ViewModel.
  • El ViewModel hace referencia al Modelo.

Es importante tener en cuenta que View y ViewModel son capaces de comunicaciones bidireccionales conocidas como Enlaces de datos.

Un ingrediente importante para la comunicación bidireccional (enlace de datos) es la interfaz INotifyPropertyChanged.

Al utilizar este mecanismo, View puede modificar los datos en ViewModel a través de la entrada del usuario, y ViewModel puede actualizar View con datos que pueden haber sido actualizados a través de procesos. en el Modelo o con datos actualizados del repositorio.

La arquitectura MVVM pone un gran énfasis en la Separación de preocupaciones para cada una de estas capas. Separar las capas nos beneficia como:

  • Modularidad: La implementación interna de cada capa se puede cambiar o intercambiar sin afectar a las demás.
  • Mayor capacidad de prueba: cada capa se puede probar por unidad con datos falsos, lo que no es posible si el código de ViewModel está escrito en el código subyacente de la vista .

La construcción:

Crear un nuevo proyecto de aplicación WPF Crear un nuevo proyecto de aplicación WPF

Cree tres carpetas nuevas en su solución: Modelo, ViewModel y View, y elimine el MainWindow.xaml original, solo para comenzar de nuevo.

Cree 3 nuevas carpetas en su solución

Cree tres elementos nuevos, cada uno correspondiente a una capa separada:

  • Haga clic con el botón derecho en la carpeta Modelo y agregue un elemento de Clase llamado HelloWorldModel.cs.
  • Haz clic derecho en la carpeta ViewModel y agrega una Clase elemento llamado HelloWorldViewModel.cs.
  • Haz clic con el botón derecho en la carpeta Ver y agregue un elemento de Ventana (WPF) llamado HelloWorldView.xaml.

Agregue los tres elementos

Modifique App.xaml para que apunte a la nueva Vista

<Application x:Class="MyMVVMProject.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:MyMVVMProject"
             StartupUri="/View/HelloWorldView.xaml">
    <Application.Resources>
         
    </Application.Resources>
</Application>

Ver modelo:

Comience con la construcción del ViewModel primero. La clase debe implementar la interfaz INotifyPropertyChanged, declarar un evento PropertyChangedEventHandler y crear un método para generar el evento (fuente: MSDN: How to Implement Property Change Notification). A continuación, declare un campo y una propiedad correspondiente, asegurándose de llamar al método OnPropertyChanged() en el descriptor de acceso set de la propiedad. El constructor del siguiente ejemplo se usa para demostrar que el Modelo proporciona los datos al ViewModel.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using MyMVVMProject.Model;

namespace MyMVVMProject.ViewModel
{
    // Implements INotifyPropertyChanged interface to support bindings
    public class HelloWorldViewModel : INotifyPropertyChanged
    {
        private string helloString;

        public event PropertyChangedEventHandler PropertyChanged;

        public string HelloString
        {
            get
            {
                return helloString;
            }
            set
            {
                helloString = value;
                OnPropertyChanged();
            }
        }

        /// <summary>
        /// Raises OnPropertychangedEvent when property changes
        /// </summary>
        /// <param name="name">String representing the property name</param>
        protected void OnPropertyChanged([CallerMemberName] string name = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }

        public HelloWorldViewModel()
        {
            HelloWorldModel helloWorldModel = new HelloWorldModel();
            helloString = helloWorldModel.ImportantInfo;
        }
    }
}

Modelo:

Luego, construye el Modelo. Como se indicó anteriormente, el Modelo proporciona datos para el ViewModel extrayéndolos de un repositorio (además de devolverlos al repositorio para guardarlos). Esto se demuestra a continuación con el método GetData(), que devolverá una simple List<string>. La lógica empresarial también se aplica en esta capa y se puede ver en el método ConcatenateData(). Este método construye la oración “¡Hola, mundo!” de la List<string> que se devolvió previamente desde nuestro “repositorio”.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MyMVVMProject.Model
{
    public class HelloWorldModel
    {
        private List<string> repositoryData;
        public string ImportantInfo
        {
            get
            {
                return ConcatenateData(repositoryData);
            }
        }

        public HelloWorldModel()
        {
            repositoryData = GetData();
        }

        /// <summary>
        /// Simulates data retrieval from a repository
        /// </summary>
        /// <returns>List of strings</returns>
        private List<string> GetData()
        {
            repositoryData = new List<string>()
            {
                "Hello",
                "world"
            };
            return repositoryData;
        }

        /// <summary>
        /// Concatenate the information from the list into a fully formed sentence.
        /// </summary>
        /// <returns>A string</returns>
        private string ConcatenateData(List<string> dataList)
        {
            string importantInfo = dataList.ElementAt(0) + ", " + dataList.ElementAt(1) + "!";
            return importantInfo;
        }
    }
}

Vista:

Finalmente, se puede construir la Vista. No es necesario agregar nada al código subyacente para este ejemplo, aunque esto puede variar según las necesidades de la aplicación. Sin embargo, se agregaron algunas líneas al XAML. La Ventana necesita una referencia al espacio de nombres ViewModel. Esto se asigna al espacio de nombres XAML xmlns:vm="clr-namespace:MyMVVMProject.ViewModel". A continuación, la ventana necesita un DataContext. Esto se establece en <vm:HelloWorldViewModel/>. Ahora la etiqueta (o el control de su elección) se puede agregar a la ventana. El punto clave en esta etapa es asegurarse de configurar Binding a la propiedad del ViewModel que desea mostrar como el contenido de la etiqueta. En este caso, es {Binding HelloString}.

Es importante vincular a la propiedad, y no al campo, ya que en este último caso la Vista no recibirá la notificación de que el valor cambió, ya que el método OnPropertyChanged() solo generará el PropertyChangedEvent en la propiedad, y no en el campo.

<Window x:Class="MyMVVMProject.View.HelloWorldView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:MyMVVMProject.View"
        xmlns:vm="clr-namespace:MyMVVMProject.ViewModel"
        mc:Ignorable="d"
        Title="HelloWorldView" Height="300" Width="300">
    <Window.DataContext>
        <vm:HelloWorldViewModel/>
    </Window.DataContext>
    <Grid>
        <Label x:Name="label" FontSize="30" Content="{Binding HelloString}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
    </Grid>
</Window>