Pasando información entre dos ViewModel en WPF

Muy buenos días a todos,

No suelo escribir sobre esta temática porque no es mi herramienta habitual de trabajo, aunque hoy quiero compartir con vosotros un tema relacionado con WPF al que le he tenido que dedicar algo de tiempo. Durante un desarrollo me ha surgido la necesidad, más que habitual por otro lado, de tener que pasar información entre dos ViewModel.

Después de buscar información por internet, comprobé que la mejor forma de hacerlo era a través de un sistema de paso de mensajes, de manera que el ViewModel origen, enviara un mensaje a un receptor, el ViewModel que necesita la información. Un artículo muy completo sobre este tema lo podéis encontrar en el siguiente enlace:

https://msdn.microsoft.com/en-us/magazine/dn745866.aspx

Por el motivo que sea, cuando quiero comprender algo, necesito hacerlo por mí mismo, y para entender la base del funcionamiento de este sistema, necesito verlo con un ejemplo muy sencillo, para luego poder usar los frameworks y librerías más avanzadas y poder entender qué hace.

Así que, no con el objetivo de reinventar la rueda, sino de comprender superficialmente como funciona un sistema de paso de mensajes, para un proyecto que tengo he creado una clase que lo implementa de una forma muy sencilla. Obviamente, es muy limitada y usando una librería como la que se sugiere en el enlace las posibilidades son muchísimas, no os recomiendo usar esta clase en un proyecto real, de dimensiones importantes. No obstante para probar, aquí tenéis, porque funcionar, funciona, y por lo que me dan a entender los tutoriales que veo sobre estos temas, de una forma más o menos parecida, así es como funcionan este tipo de sistemas. Os comparto la clase y os explico como funciona.

    public sealed class MessageHelper
    {
        private static readonly MessageHelper instance = new MessageHelper();

        private Dictionary<object, object> messageCollector; 

        private MessageHelper()
        {
            messageCollector = new Dictionary<object, object>();
        }

        public static MessageHelper Instance
        {
            get
            {
                return instance;
            }
        }

        public void SendMessage<T>(T messageToRegister)
        {
            T aux = Activator.CreateInstance<T>();

            if (!messageCollector.Keys.Contains(aux.GetType()))
                messageCollector.Add(aux.GetType(), messageToRegister);
            else
                messageCollector[aux.GetType()] = messageToRegister;
        }

        public T ReceiveMessage<T>()
        {
            T aux = Activator.CreateInstance<T>();
            return (T)messageColector[aux.GetType()];
        }
    }

El primer punto destacable es que he implementado para la clase el patrón Singleton, y resulta obvio el motivo y además la misma está sellada para que no se pueda heredar.

Y lo único que he hecho en esta clase es crear dos métodos, uno de envío y otro de recepción de mensajes, que usan un diccionario de tipo clave=>valor para el almacenamiento y lectura de los mensajes. Para permitir que cualquier objeto pueda ser enviado y recibido como mensaje, he creado los métodos con un tipo genérico, lo que me da mucha libertad a la hora de usarla posteriormente.

¿Y cómo se usa esta clase?

Pues el funcionamiento es muy sencillo, como he dicho, podemos usar cualquier objeto para pasar el mensaje, aunque en mi caso estoy definiendo algunos objetos particulares con el mensaje que quiero pasar.

public class ExampleMessage
{
     public string MessageData;
     public int OtherMessageData;
}

Y lo usaríamos de la siguiente forma, he unido para el caso, las funciones de envío y recepción del mensaje, pero éstas, obviamente, irían en cada uno de los ViewModels que necesitáramos.

public void SendMessageFunction(string messageData, int otherMessageData)
{
    MessageHelper.Instance.SendMessage<ExampleMessage>(new ExampleMessage{ 
                                                  MessageData = messageData,
                                                  OtherMessageData = otherMessageData
                                              });
}

public void SendMessageFunction()
{
    string message = MessageHelper.Instance.ReceiveMessage<ExampleMessage>().MessageData;
    int other = MessageHelper.Instance.ReceiveMessage<ExampleMessage>().OtherMessageData;
}

Y nada más, espero que os ayude a comprender, si es que os era necesario, este tipo de sistemas. Yo como os decía, a veces para comprenderlos y comenzar a utilizarlos, necesito antes clases sencillas y hechas por mi en las que vea como funciona lo que estoy viendo en sistemas más complejos.

Un saludo y buen fin de semana a todos.

Anuncios

Entendiendo el patrón de diseño MVVM (II)

Buenas a todos,

Hace unos días escribí un post hablando sobre lo que había aprendido del patrón MVVM y lo que me había parecido una vez que lo comprendí.

Entendiendo el patrón de diseño MVVM

En el post, terminaba emplazándoos a una nueva ocasión en la que os mostraría un ejemplo sobre cómo aplicar este patrón en WPF. Y por fin hoy, después de unos días trabajando en él os lo puedo traer. A continuación os voy mostrar e intentar explicar lo máximo posible el ejemplo de la aplicación del patrón de diseño Model-View-ViewModel. Vamos a ir poco a poco avanzando en el ejemplo.

Lo primero que debemos hacer es dotar a nuestra aplicación de un pequeño “Core”, que nos proporcione las clases que necesitamos para trabajar con MVVM. En mi caso, y al estar empezando a trabajar con esto, he decidido utilizar unas clases que he encontrado en otro blog. A medida que vaya profundizando en el conocimiento de este patron de diseño y las clases crearé unas propias o si es necesario mejoraré las que ya he tomado como partida. Este es el blog donde se encuentran las clases que he cogido:

http://www.c-sharpcorner.com/UploadFile/1a81c5/a-simple-wpf-application-implementing-mvvm/

Necesitamos como partida dos clases básicamente: BaseViewModel y DelegateCommand. La primera nos servirá como clase base para todos los ViewModel que haya que definir mientras que la segunda nos permite crear los comandos que después serán utilizados por la vista.

El ejemplo que voy a crear a continuación es muy sencillo, lo que quiero hacer es crear una ventana que accede a dos modelos distintos. Para uno de ellos mostrará una lista a la que se podrán añadir nuevos elementos y para el otro permitirá indicar los valores del modelo y mostrarlos después en una ventana emergente. Para la funcionalidad se necesitarán dos comandos distintos, uno que añadirá elementos a la lista y otro que mostrará la ventana emergente.

Definiendo nuestros modelos

Para el ejemplo he creado dos modelos simples que os mostraré a continuación: Worker y Contact

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

namespace ExampleApplication
{
    public class Worker
    {
        public string name { get; set; }
        public string department { get; set; }
        public double amount { get; set; }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ExampleApplication
{
    public class Contact
    {
        public string phone { get; set; }
        public string email { get; set; }
    }
}

Definiendo el ViewModel

El ViewModel, como definíamos en el artículo anterior, será el almacén que contendrá los modelos y comandos que usará la vista.

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;

namespace ExampleApplication
{
    public class ViewModel : BaseViewModel
    {
        private Worker _worker;
        private ObservableCollection<Contact> _contacts;
        private Contact _contact;
        private ICommand _addContactCommand;
        private ICommand _showWorker;

        public Worker Worker
        {
            get
            {
                return _worker;
            }
            set
            {
                _worker = value;
                NotifyPropertyChanged("Worker");
            }
        }

        public ObservableCollection<Contact> Contacts
        {
            get
            {
                return _contacts;
            }
            set
            {
                _contacts = value;
                NotifyPropertyChanged("Contacts");
            }
        }
        
        public Contact Contact
        {
            get
            {
                return _contact;
            }
            set
            {
                _contact = value;
                NotifyPropertyChanged("Contact");
            }
        }

        public ICommand AddContactCommand
        {
            get 
            {
                if (_addContactCommand == null)
                    _addContactCommand = new DelegateCommand(param => this.AddContact(),null);

                return _addContactCommand;
            }
        }

        public ICommand ShowWorkerCommand
        {
            get
            {
                if (_showWorker == null)
                    _showWorker = new DelegateCommand(param => this.ShowWorker(), null);

                return _showWorker;
            }
        }

        public ViewModel()
        {
            Worker = new Worker();
            Contact = new Contact();
            Contacts = new ObservableCollection<Contact>()
            {
                new Contact(){phone = "950123123", email = "a@b.com"},
                new Contact(){phone = "951321321", email = "b@c.com"}
            };
            Contacts.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(ContactCollectionChanged);      
        }

        void ContactCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            NotifyPropertyChanged("Contacts");
        }

        private void AddContact()
        {
            Contacts.Add(Contact);
            Contact = new Contact();
        }

        private void ShowWorker()
        {
            MessageBox.Show(Worker.name + Worker.department + Worker.amount.ToString(),"WorkerData");
        }

    }
}

Vamos a ver algunos aspectos a tener en cuenta de esta clase:

  • En el constructor se inicializan los modelos que se van a utilizar
  • En la clase se definen como propiedades tanto los modelos, como los comandos que se van a utilizar, y sus descriptores de acceso respectivos. En el caso de los modelos, con el descriptor set se llama al método NotifyPropertyChanged para indicar que ha habido un cambio en dicho modelo.
  • Los comandos se asocian a un método privado a través de la clase de nuestro “core” que hemos creado previamente. La clase DelegateCommand

Definiendo la vista

Por último la vista, aquí es donde veremos las capacidades del binding, tanto de datos como de comandos que nos ofrece WPF, veremos como se conectan tanto los modelos como los comandos del ViewModel con la vista. Primero vamos a ver el código XAML

<Window x:Class="ExampleApplication.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:viewmodel="clr-namespace:ExampleApplication"
        Title="MainWindow" Height="500" Width="525">
    <Window.Resources>
        <viewmodel:ViewModel x:Key="ViewModel"/>
    </Window.Resources>
    <Grid DataContext="{Binding Source={StaticResource ViewModel}}">
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition />
        </Grid.RowDefinitions>
        <DataGrid HorizontalAlignment="Center" VerticalAlignment="Center" ItemsSource="{Binding Contacts}" Grid.Row="0" Grid.Column="0"/>
        <StackPanel Orientation="Horizontal" Grid.Row="1" Grid.Column="0" Margin="0 10">
            <Label Content="Phone"></Label>
            <TextBox Text="{Binding Contact.phone}" Width="100"></TextBox>
            <Label Content="Email"></Label>
            <TextBox Text="{Binding Contact.email}" Width="100"></TextBox>
            <Button Content="Add Contact" Command="{Binding AddContactCommand}" Margin="10 0 0 0"></Button>
        </StackPanel>
        <StackPanel Orientation="Horizontal" Grid.Row="2" Grid.Column="0" Margin="0 10">
            <Label Content="Name"></Label>
            <TextBox Text="{Binding Worker.name}" Width="100"></TextBox>
            <Label Content="Department"></Label>
            <TextBox Text="{Binding Worker.department}"  Width="100"></TextBox>
            <Label Content="Amount"></Label>
            <TextBox Text="{Binding Worker.amount}"  Width="50"></TextBox>
            <Button Content="Show Worker" Command="{Binding ShowWorkerCommand}" Margin="5 0 0 0"></Button>
        </StackPanel>
    </Grid>
</Window>
  • Inicialmente debemos de indicar el ViewModel al que vamos a hacer referencia en la vista. (Líneas 6 a 8)
  • En segundo lugar, debemos de indicar el DataContext del Grid de la vista, indicándolo como StaticResource y haciendo referencia al ViewModel, veremos a partir de ahora que la información de nuestro ViewModel aparece en el IntelliSense de la vista. (Línea 9)
  • Cuando queramos asociar una propiedad de alguno de los modelos del ViewModel a uno de los controles de la vista, tendremos que hacerlo por medio del Binding, indicando el modelo y su propiedad. (Línea 22)
  • Por último, podemos asociar también los botones de la vista a comandos del ViewModel por medio de la propiedad “Command” de los mismos. (Línea 25)

Y para terminar el code-behind de la vista

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace ExampleApplication
{
    /// <summary>
    /// Lógica de interacción para MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    }
}

¿Y qué pasa con el code-behind?, pues como se puede ver, no hay nada. Con esto, cumplimos uno de los objetivos y beneficios de este patrón de diseño que os comentaba en el artículo anterior, separar el diseño, de la lógica y la funcionalidad. De esta forma un diseñador puede dedicarse exclusivamente al look&feel de la aplicación sin centrarse en nada de su funcionalidad, ni eventos de los botones ni nada, todo queda en el ViewModel. ¿Y el funcionamiento del mini ejemplo que hemos hecho?, pues os dejo algunos capturas a continuación, para que podáis verlo.

Captura de pantalla 2014-11-29 a las 23.07.52

Captura de pantalla 2014-11-29 a las 23.07.52

Captura de pantalla 2014-11-29 a las 23.08.12

Y nada más, espero que os sea de utilidad el código y os pueda servir como guía para aplicar este patrón de diseño en aplicaciones de escritorio basadas en WPF.

Saludos

Entendiendo el patrón de diseño MVVM

Buenas a todos,

Hoy quiero intentar explicar en qué consiste este patrón de diseño con el que me he encontrado cuando he empezado a desarrollar aplicaciones de escritorio con Windows Presentation Foundation (WPF) y de paso empezar a contar cosas sobre cómo me va con este entorno de desarrollo. Voy a intentar explicarlo tal y como yo lo he entendido. Si bien inicialmente puede parecer algo complejo de entender, o a mi me lo pareció, cuando le prestas algo de atención te das cuenta de que no es algo muy lejano al patrón MVC del que tanto oímos hablar y es archiconocido actualmente.

Este patrón de diseño es técnicamente una implementación del patrón de diseño Presentation Model de Martin Fowler que también implementa AngularJS o KnockoutJS y que es propuesto para ser usado en el desarrollo de aplicaciones WPF y Siverlight.

Para explicar en qué consiste este patrón, voy a sacar un extracto de la información que he encontrado en la web en estos días y de la que os dejo también el enlace.

El patrón MVVM proporciona una separación entre la interfaz gráfica y el desarrollo de la lógica de negocio. El ViewModel del patrón MVVM es el responsable de exponer los objetos del modelo que podrá consumir la vista de manera que estos objetos serán fácilmente manejables y consumidos. MVVM usa las funciones de DataBinding que nos proporciona XAML, para separar estas dos capas consiguiendo eliminar el code-behind de las vistas y facilitar el testing del código de los eventos que pude lanzar una vista, ya que también se encarga de gestionarlos.

Por tanto el ViewModel se puede entender como un contenedor de datos y comandos que no sabe de donde vienen los datos (aspecto del que se encargará el modelo) y cómo estos serán mostrados (aspecto del que se encargará la vista)

http://en.wikipedia.org/wiki/Model_View_ViewModel

http://www.codeproject.com/Articles/126249/MVVM-Pattern-in-WPF-A-Simple-Tutorial-for-Absolute

http://www.c-sharpcorner.com/UploadFile/1a81c5/a-simple-wpf-application-implementing-mvvm/

http://www.markwithall.com/programming/2013/03/01/worlds-simplest-csharp-wpf-mvvm-example.html

Vamos a ver una imagen de la arquitectura típica de este patrón de diseño.

MVVM

En definitiva, podemos ver que la vista conoce al viewmodel y los modelos y comandos que éste nos ofrece y el viewmodel conoce el modelo de datos. Para los que habitualmente habéis trabajado con MVC, como yo, os surgirá en seguida la pregunta de ¿Y no es el ViewModel un controlador?, pues técnicamente no, pero se le parece, de hecho, los controladores no tienen que desaparecer y en aplicaciones que por su tamaño, requieran separar adecuadamente la lógica, es posible que sigan existiendo esos controladores que la implementen, y que podrán ser usados por los distintos ViewModels a través de los comandos que haya definidos en éstos, en estos casos podemos decir que tenemos un patrón MVCVM.

Para terminar y a modo de resumen, la clave de este patrón de diseño, los ViewModels, se pueden entender como una capa intermedia entre las vistas y la lógica de la aplicación, que nos permite desacoplar completamente el diseño del código necesario para la lógica del mismo, eliminando el code-behind de la vista y proporcionando una forma eficaz para testar dicha lógica. Esto permitirá estructurar de mejor forma nuestro desarrollo, separando y diferenciando adecuadamente los roles de diseñador y desarrollador.

En el próximo post os enseñaré un ejemplo de cómo se puede implementar este patrón en una aplicación WPF, que imagino que terminará de disipar las dudas sobre el MVVM.

Un saludo y espero que os sea útil.