Patrones de diseño en Siverlight: Model View ViewModel

Silverlight Add comments

Es verdad que tenía un poco abandonado el blog, y puede parecer que Silverlight también. Lo primero es cierto, pero lo segundo todo lo contrario, ya que en este tiempo he estado asignado a varios proyectos Silverlight. Con un poco de suerte cuaja este nuevo cacharro de Microsoft y las empresas se toman en serio esta nueva tecnología.

Y ahora, cuando más de uno está destripando PRISM, como  por ejemplo el compañero Braulio Diez, que por cierto ha sido nombrado recientemente MVP en Silverlight, llega  el menda con un post de MVVM. Desde aquí, y  antes de continuar, dar mi más sincera enhorabuena a Braulio por este reconocimiento – no sé realmente cuanta gente opta a estos premios, pero si estoy seguro que nadie se lo merecía más que él.  Todo un lujo trabajar a su lado. Congratulations (es que se empeña en que escriba en ingles :-) ).

MCV, MVP, y ahora MVVM, Model View ViewModel. Aunque ya se ha dicho y escrito mucho sobre este modelo,  voy a intentar dar una visión más práctica,  después de haber aplicado el modelo en diversas aplicaciones.

Un poco de teoría e historia

  1. MVVM es un patrón de diseño especialmente diseñado para las  aplicaciones definidas con WPF.
  2. Un patrón de diseño nos tiene que  ayudar a definir la arquitectura a la hora de diseñar una aplicación, identificando problemas y aportando soluciones.

En el caso que nos atañe, MVVM, es un patrón definido a partir del patrón MVC (Model View Controller) y MVP (Model View Presenter), de los que claramente identificamos ciertas partes en común.

  • Model: Identifica a la lógica de negocio. Acceso a datos, y operaciones sobre los mismos
  • View: Define a la Interfaz que ve el usuario.
  • Controller / Presenter: Gestiona las operaciones que efectúa el usuario sobre la Vista, accediendo al Model si la operación lo requiere.

Teniendo en cuenta  estas definiciones, tenemos que destacar unas pequeñas diferencias con respecto a MVVM. Un detalle que no podemos dejar pasar por alto es el hecho de que  una aplicación Silverlight está definida entre el Cliente y el Servidor pero podemos situarla más cerca de una aplicación winform que de una aplicación web. Teniendo esto en mente definamos cada una de las partes que componen el patrón.

  • View: Al igual que en MVC y MVP, la View siguen siendo la Interfaz de Usuario, está definida en  XAML  (aunque también es  posible definirla  en el codebehind asociado).  La vista mostrara datos y generará eventos como respuesta a las acciones del usuario. Un dato importante, los controles estarán enlazados al modelo mediante Data Binding.
  • ViewModel: Será el encargado de manejar los eventos que genera la View, ya sea recogiendo o mostrando datos.  Es la capa intermedia del patrón que comunicara el Modelo con la Vista. Esta capa será la encargada de preparar los datos del modelo para que puedan ser consumidos por la Vista.
  • Model: Esta capa será la encargada de mantener la persistencia de los datos. En Silverlight esto se hace mediante Web Services. El modelo de datos, lo obtendremos al generar la referencia sobre  los servicios web, definiendo automáticamente un clase proxy para tal efecto. 

La idea fundamental de los modelos en capas es que cada una de las partes tengan mínimo acoplamiento y máxima cohesión, es decir cada una de ellas puedas existir perfectamente pero a su vez la conjunción de todas ellas funcione de una manera óptima.

En un primer momento podríamos decir que MVVM es MVP/C con otro nombre, pero no, hay algo más. Para que todo esto  funcione necesitamos descubrir  el  engranaje que se escode detrás de cada una de las ruedas que forman el reloj.

Binding Automático.

El Data Binding, esa es la gran diferencia. Esta característica que nos ofrece WPF es la encargada de enlazar la interfaz con el modelo o contexto de datos, de tal manera que un cambio en el modelo de datos se replique automáticamente en la interfaz y a su vez un cambio en la interfaz se transfiera de forma automática sobre el modelo. Y todo esto sin hacer prácticamente nada J.

Arquitectura de un proyecto.

Una aplicación Silverlight (solución de Visual Studio) la podemos dividir en dos partes. Una parte en Cliente, la aplicación en sí, y otra en Servidor, el  acceso y  el modelo de datos.  Esta podría ser la arquitectura de una aplicación Silverlight.

   

Manos a la obra.

Una vez hayamos creado el armazón de nuestro proyecto y tengamos cada uno de las capas definidas, pasemos a ver de qué estará compuesta  cada una de ellas. Voy a describir cada una de ellas tal vez a primera vista desordenado, pero es  el orden lógico que sigo personalmente, el cual seguramente no sea el mejor, pero como dije al principio, el artículo se basa en la experiencia obtenida en el desarrollo de varias aplicaciones con el patrón Model View ViewModel. Por lo tanto el orden en que voy a explicar las capas será el siguiente:

1.       Client.View

2.       Server.Entities

3.       Server.DataLayer

4.       Server.Web

5.       Client.Entities

6.       Client.Model

7.       Client.ViewModel

Client.View

Como su nombre indica, aquí encontraremos las interfaces de usuario. Tendremos la página principal, y pagina auxiliares (si hacemos uso del Navigation Framework, como es en nuestro caso). También tendremos los controles de usuario que definamos.

Un objeto View estará definido por un fichero XAML y un fichero code begind asociado. La idea principal es que la interfaz quede casi totalmente definida en el fichero XAML, estableciendo declarativamente el binding de los datos de nuestro modelo y el de otras propiedades de los controles. Cuando decimos “casi” es por el hecho de que no nos queda más remedio que manejar los eventos que queramos manejar en el fichero code behind (a no ser que hagamos uso por ejemplo de PRISM). En cualquier caso, nos quedará una capa de Vista bastante limpia.

Cada View tendrá asociado como data context un ViewModel. Las propiedades del objeto ViewModel serán las que luego usaremos en el data binding.

Cosas a destacar. El objeto DataContext que establezcamos en una View será el usado por cada uno de los controles que incluya la Vista, siempre y cuando que no establezcamos un DataConext particular para ellos. Es decir un control buscará si tiene asociado DataContext, y si no, buscará en su  contendor padre y así sucesivamente.

Es muy importante que tengamos claro cómo vamos a maquetar nuestra aplicación, paginas y controles de usuario,  ya que en función de esto más tarde intentaremos preparar los objetos de nuestro modelo para facilitarnos el trabajo.

Server.Entities

No pensemos que vamos a definir los objetos entidades en función de las interfaces. Si se ha entendido eso, me he explicado mal. El modelo  de entidades representa los objetos que definen un  problema, pero sí podemos modelarlos de manera  que luego nos sea mas fácil trabajar con ellos.

Podemos tener una View definida con cuatro controles de usuario. Por lo tanto podemos tener un objeto con cuatro propiedades que a su vez definen objetos, las cuales usaremos como data context particular para cada uno de esos controles de usuario.  Esto es solo una forma de trabajar, nada pasaría por tener un objeto que agrupe las propiedades de los 4 subentidades y establecer solamente el data context en el objeto View, dejando que los controles hereden de ella.

Si hacemos uso de entidades complejas (es decir entidades compuestas por sub entidades), para evitar problemas es conveniente que establezcamos los atributos [DataMember] y [DataContract]  a nuestras clases y atributos.

Server.DataLayer

Aqui tendremos las clases que definan las operaciones que podemos hacer sobre los las entidades  de nuestro modelo. Será la capa encargada del acceso a las base de datos.  Estas clases serán accedidas por el servicio web.

Server.Web

Este proyecto, además de tener las páginas web que alojarán  la aplicación Silverlight, definirá los web services que expondrá tanto el modelo de datos del Servidor  a la aplicación Silverlight en cliente como las operaciones que podremos realizar sobre ellos. El servicio web mediante una instancia de un objeto del  DataLayer obtendrá el modelo de datos.

Client.Entities

Es nn esta capa donde vamos a añadir la referencia al servicio web creado. Una vez que añadamos esta referencia se creará una clase proxy con todas los operaciónes que ofrece  el servicio web y todas las entidades que exponen esas operaciones, creando en cliente el mismo modelo de datos definido en el Servidor.

Todas las entidades extra que necesitemos en Cliente, las añadiremos aquí. Además podemos ampliar el modelo de entidades definidas en Servidor con nuevas entidades. Podremos añadir nuevas propiedades a las entidades existentes, o incluso nos  podemos encontrar en la situación de que las propiedades además de establecer y obtener su valor, tiene que realizar alguna operación, para lo cual no nos queda más remedio que implementar una nueva propiedad para tal efecto. Esto lo haremos añadiendo clases parciales al proyecto de entidades.

C#:
  1. <p class="MsoNormal" style="text-align: justify; margin: 0cm 0cm 10pt;"><span style="font-size: small; font-family: Calibri;">namespace CV_MVVM.Client.Entities.CV_MVVM_Service
  2. {
  3.    public partial class CV
  4.    {
  5.         private bool _dataChanged = false;</span>
  6.  
  7.        //Reescribimos una propiedad pero añadiendo funcionalidad.
  8.        //En este caso tenemos un flag para indicar que
  9.        //los datos han sido modificados
  10.        public String Name_ToBind
  11.        {
  12.            get { return Name; }
  13.            set
  14.            {
  15.               Name = value;
  16.              //Indicamos que se ha producido un cambio en el valor
  17.              _dataChanged = true;
  18.              RaisePropertyChanged("Name_ToBind");
  19.            }
  20.        }
  21.  
  22.     }
  23. }

La capa de Entities es  el  canal de comunicación común entre el Model y el ViewModel.

Client.Model

Esta capa es la encargada de establecer la comunicación entre Silverlight y los servicios Web ya sea solicitando datos como realizando operaciones sobre ellos (añadir, modificar, eliminar). Para ello las clases del modelo contendrán los métodos necesarios para realizar estas operaciones.

Destripando una clase del  Modelo

Como bien sabemos el modo de comunicación entre Silverlight y el Servidor es mediante Servicios Web, y la respuesta de estos es asíncrona. Esto quiere decir que tras realizar una llamada a un método del servicio web, la respuesta no es inmediata, continuando el flujo de la aplicación. Las respuestas son encoladas ejecutadas en función al orden de llegada. Para establecer un control sobre estas respuestas y controlar pues las acciones a realizar lo que hacemos es añadir eventos públicos a nuestra clase Model. Posteriormente cuando las repuestas “asíncronas” de los web services son procesadas lazamos el evento que hemos creado en repuesta a la peticicion efectuada. Será obligación del ViewModel subscribirse y manejar  estos eventos.

C#:
  1. public class CVModel
  2. {
  3.  
  4.      public CVModel()
  5.      {
  6.      }
  7.  
  8.      public event EventHandler evCVRecieved;
  9.  
  10.      public void GetCV(string name)
  11.      {
  12.         BasicHttpBinding binding = new BasicHttpBinding();
  13.  
  14.         EndpointAddress address = new EndpointAddress("<a href="http://localhost:1046/Services/CVService.svc">http://localhost:1046/Services/CVService.svc</a>");
  15.  
  16.         CVServiceClient client = new CVServiceClient(binding, address);
  17.  
  18.        //Indicar funcion que va a capturar la repuesta del webservice
  19.        client.GetCVCompleted += new EventHandler&lt;GetCVCompletedEventArgs&gt;(client_GetCVCompleted);
  20.  
  21.        //Generar llamada al webservice
  22.        client.GetCVAsync(name);
  23.      }
  24.  
  25.      /// &lt;summary&gt;
  26.     /// Manejar repuesta del web service
  27.     /// &lt;/summary&gt;
  28.     void client_GetCVCompleted(object sender, GetCVCompletedEventArgs e)
  29.     {
  30.        if (e.Result != null)
  31.            evCVRecieved(this, e);
  32.     }
  33.  
  34. }

Client.ViewModel

Como ya dijimos anteriormente esta capa es la encargada tanto de manejar los eventos sobre la View como de  ofrecer los datos consumidos por esta.  Teniendo esto en cuenta, una clase ViewModel tendrá métodos para manejar las peticiones de la View y “Propiedades” que expondrán los datos requeridos por esta.  Además tendremos manejadores para los eventos a los que la clase ViewModel se subscriba (eventos lanzados por el Model).

Interfaz IPropertyNotifyChanged.

Es interesante hacer que nuestra clases ViewModel implementen la interfaz IPropertyNotifyChanged, de manera que cambios completos sobre las propiedades, es decir que cambiemos el objeto que una propiedad devuelve, se reproduzcan sobre la vista. Tal vez no queda del todo claro este punto, pongamos un ejemplo.

Si por ejemplo tenemos una propiedad “ListaProyectos”, y en nuestra clase ViewModel no implementa la interfaz IPropertyNotifyChanged siempre que queramos establecer la lista por completo tendremos que limpiar su contenido e ir añadiendo uno a uno los elementos. No podemos cambiar simplemente el objeto lista que devuelve la propiedad, ya que con eso no conseguimos que se lanze el evento de nuevo elemento que se produce al añadir elementos sobre una lista de tipo ObservableCollection.

Si por el contrario implementamos la interfaz IPropertyNotifyChanged, simplemente tendriamos que definir los Get y Set para los propiedades y en la defnición del Set además de establecer el valor lanzaríamos el evento PropertyChanged para que la vista fuera notificada del cambio.

C#:
  1. public class CVModel
  2. {
  3.  
  4.     public CVModel()    {}
  5.  
  6.     public event EventHandler evCVRecieved;
  7.  
  8.     public void GetCV(string name)
  9.     {
  10.         BasicHttpBinding binding = new BasicHttpBinding();
  11.  
  12.         EndpointAddress address = new EndpointAddress("http://localhost:1046/Services/CVService.svc");
  13.  
  14.         CVServiceClient client = new CVServiceClient(binding, address);
  15.  
  16.        //Indicar funcion que va a capturar la repuesta del webservice
  17.        client.GetCVCompleted += new EventHandler(client_GetCVCompleted);
  18.  
  19.        //Generar llamada al webservice
  20.        client.GetCVAsync(name);
  21.     }
  22.  
  23.     ///
  24.     /// Manejar repuesta del web service
  25.     ///
  26.     void client_GetCVCompleted(object sender, GetCVCompletedEventArgs e)
  27.     {
  28.          if (e.Result != null)
  29.              evCVRecieved(this, e);
  30.     }
  31.  
  32. }

Terminando...

Siguiendo este flujo hemos conseguido montar una primera instancia de nuestro proyecto, sobre el cual podremos ir trabajando y completando. Tenemos el modelo definido en lado del servidor servidor, en la parte cliente [Client.Entities], tenemos los metodos de acceso a datos sobre el servidor [Client.Model] y tenemos las propiedades que ofrecen los datos a mostrar en las interfaces [Client.ViewModel y Client.View].

Bueno con esta pequeña guia simplemente intento mostrar un punto de entrada al patrón a aquellas personas que se puedan encontrar un poco perdidas entre el data binding y el patrón Model View ViewModel. 

4 Responses to “Patrones de diseño en Siverlight: Model View ViewModel”

  1. Lobosoft Says:

    Muy interesante el artículo. La verdad es que resulta bastante esclarecedor para aquellos que, ahora que estamos desvinculados del desarrollo web, no queremos perder el hilo de las nuevas tecnologías que comienzan a coger fuerza en este campo.

    ¡Un saludo!

  2. OsFoEl Says:

    De los articulos mas interesantes de MVVM.
    Simplemente hay ejemplos muy sencillos y por fin alguien da una vision mas profunda.

  3. diseño web multimedia Says:

    gracias por compartir este artículo.

  4. Jonas Jimenez Jimenez Says:

    Muy Interesante el articulo aunque seria bueno que nos proporcionaras un ejemplo aplicando este Padron de Diseño, en mi caso no he comenzado a utilizar Siverlight, pero los proyectos que estoy realizando con VS2008 tengo las siguientes capas: Entidades, Persistencia,Control y Vista.

    El comportamiento es el siguiente:

    Entidades aqui basicamente hago una abstraccion de las Tablas de BD, obviamentes este Capa es un Proyecto de Clases.

    Persistencia: aqui Modelo las Acciones de BD y ocupo Entidades. Insertar, Actualiza,Obtener, Obtener Todos etc.

    Contro: Aqui por decirlo asi, cacho los Evento de la Capa de Vista, y mando hablar la Persistencia Correspondiente.

    Vista: Es la Interfaz de Usuario.

    Te agradeceria si puedes proporcionarme un Ejemplo de Este Articulo, y estemos en retroalimentacion con conocimiento.

    Saludos!! desde Tabasco.

Leave a Reply

WP Theme & Icons by N.Design Studio
Entries RSS Comments RSS Acceder

Switch to our mobile site