Archive

Archive for the ‘MVVM’ Category

Día 17: Usando Windows Azure

December 13th, 2011 No comments

Esta es una traducción Day 17: Using Windows Azure, puedes encontrarlo aquí en la versión original en inglés.

dia17

Windows Phone + Windows Azure = Mejor unidos

Algunas de las mas interesantes aplicaciones de Windows Phone utilizarán servicios de algún tipo. Esos servicios podrían ser servicios web para dar acceso a la lógica de negocio, así como en aplicaciones ASP.NET tradicionales. Esos servicios podrían también ser servicios de administración de identidad, dando un camino para manejar la autenticación de nuestra aplicación.

Modo tradicional

Típicamente nosotros levantamos uno o mas servidores y después desplegamos los servicios e información en ellos. Hacerlo así puede ser un proceso costoso – en tiempo y dinero –. Necesitamos tomar el tiempo para construir, configurar y asegurar los servicios. También necesitamos mantener estos servidores (hardware, fallos, reparaciones de sistema, etc.). Prediciendo cuantos servidores y que tamaño necesitamos para soportar nuestra aplicación es igualmente un reto. Comprar demasiado hará que hayamos gastado el dinero de alguien. Comprar muy pocos hará a los usuarios (y a los jefes) molestarse. Todo esto nos forza como desarrolladores a preocuparnos por la infraestructura, cuando todo lo que queremos es construir el siguiente hit de aplicaciones móviles.

Modo moderno

Una de las tendencias prevalecientes ahora es la de utilizar servicios (nota “servicios” y no “servidores”) otorgados por una plataforma en la nube. La plataforma Windows Azure nos da los servicios que nos permiten el despliegue de nuestros servicios web. Escribimos la aplicación y la desplegamos –y dejamos a Windows Azure hacer el resto-. Windows Azure también ofrece acceso a  servicios de almacenamiento escalables y altamente disponibles en forma de tablas, blobs y colas.

* Las tablas son un mecanismo semi-estructurado de almacenamiento que es capaz de almacenar información masiva muy eficiente (piensa en NoSQL, no una base de datos relacional).

* Los blobs esencialmente actúan como un sistema de archivos gigante para almacenar lo que tu quieras (imágenes, películas, documentos, etc.).

* Las colas sirven como un mecanismo de mensajes de peso ligero para pasar información entre sistemas desconectados.

Adicionalmente, Windows Azure ofrece un servicio de administración de identidad, los Servicios de control de acceso (ACS pos sus siglas en inglés) que nos da una forma fácil de autenticar a los usuarios vía proveedores de identidad múltiples. ACS viene pre configurado para soportar mayores redes sociales, así como Facebook, Yahoo!, Windows Live ID y Google. ACS puede igualmente dar “tap”en una empresa vía Active Directroy, Fefederation Services (ADSFv2). ACS es específicamente genial para aplicaciones móviles si es que los usuarios tienen ya un perfil social de algún tipo. De hecho, los usuarios de Windows Phone deben tener ya un Windows Live ID.

Hay numerosos beneficios para utilizar una plataforma como Windows Azure para crear tus aplicaciones Windows Phone. Para los iniciantes, es rápido. Puedes usar Visual Studio para escribir ambos servicios, tanto Windows Phone como Windows Azure. Una vez que el código esté escrito, el servicio puede estar disponible en internet en cuestión de un par de minutos. Usar Windows Azure puede resultar igualmente muy barato. Puedes almacenar tanta información como tu quieras comenzando por solo $0.14 USD/GB al mes. Debido a que todo el desarrollo puede ser hecho localmente en tu máquina de desarrollo, no necesitas incluso comenzar a pagar sino hasta que comience a correr tu servicio en la nube. Todo esto nos permite enfocarnos en crear aplicaciones geniales para Windows Phone y los servicios pueden hacerla mas ligera, además de no preocuparnos por la infraestructura.

Vamos a hacerlo

Vamos a darle un vistazo rápido al flujo de como construir una muy simple aplicación para Windows Phone que cuente con servicios disponibles en la plataforma Windows Azure.

Architecture v3

Algunos de los aspectos principales de esta arquitectura y flujo de información incluyen:

1.- La necesidad de autenticar a los usuarios de la aplicación de Windows Phone. Las redes sociales mas grandes como Facebook, Windows Live, Yahoo! y Google parecen las opciones lógicas. Dejaremos que los Windows Azure Access Control Services(ACS) maneje ese trabajo (mas detalles abajo).

2.- Un servicio WCF REST que servirá como el sostén principal de la lógica de aplicación y provee el acceso seguro a nuestra información.

Una cosa para apuntar aquí es como el teléfono está accesando a la tabla y blob en Winsows Azure. La API nativa para Windows Azure es una API REST. Esto incluye acceder al almacenamiento así como a las tablas y blobs. Uno de los aspectos de seguridad del almacenamiento de Windows Azure es que todos los accesos están protegidos por llaves de acceso. La llave de acceso necesita ser enviada en cada solicitud (parte de la llamada REST) a Windows Azure. Estas llaver podrían ser mantenidas en secreto. Dado que queremos mantener nuestros secretos, un secreto, no queremos poner las llaves de acceso en el teléfono. Hacer esto dejaría a nuestra información secreta irse libremente, y eso podría ser malo para nosotros. Es posible cambiar las llaves de acceso (p. ej. si las llaves fueron comprometidas), y si cambiaron, necesitaríamos actualizar la aplicación para leer la nueva llave. Para estar seguros, creamos un servicios web proxy por el cual todas la solicitudes al almacenamiento serán canalizadas. Esto mantiene nuestra información secreta así y nos permite desarrollar un servicio en una forma muy SOA.

Hay dos formas básicas en las cuales podemos crear este servicio web. Los amigos geniales en microsoft, han creado el Windows Azure Toolkit for Windows Phone el cual provee amplios recursos y ejemplos para escribir aplicaciones de Windows Phone que consuman servicios de Windows Azure. Incluido en este toolkit hay plantillas de proyectos las cuales se saltan todo el proceso para trabajar con ACS, almacenamiento (tablas, blobs y colas) e incluso recipientes para Push Notifications.

Sin embargo, para los propósitos de este artículo, vamos a crear un servicio WCF REST que vamos a poder consumir fácilmente en nuestra aplicación Windows Phone. El servicio WCF no solo sirve como puente para acceder al almacenamiento de Windows Azure, sino que también contendrá nuestra lógica. Si quisiéramos podríamos extender el servicio WCF para otros propósitos también –tales como alimentar una aplicación ASP.NET o incluso aplicaciones para otras plataformas móviles-. No nos meteremos mucho en varios aspectos del almacenamiento de Windows Azure aquí, dado que eso esta cubierto en profundidad en el Windows Azure Training Kit (checa el módulo de Explorando el almacenamiento de Windows Azure).

Almacenamiento de tablas.

Para comenzar, necesitamos una entidad básica que almacenaremos en el almacenamiento de tablas de Windows Azure.

using System;
using Microsoft.WindowsAzure.StorageClient;

namespace Servicio.Entidades
{
public class Coche : TableServiceEntity
{
public Coche()
{
}

public Coche(string make, string modelo, int anio, string descripcion, string imagenUrl)
{
PartitionKey = make;
RowKey = DateTime.UtcNow.Ticks.ToString();
Make = make;
Modelo = modelo;
Anio = anio;
Descripcion = descripcion;
DireccionImagen = imagenUrl;

}

public string Make { get; set; }
public string Modelo { get; set; }
public int Anio { get; set; }
public string Descripcion { get; set; }
private string DireccionImagen { get; set; }

}
}

Hasta aquí podemos crear un método simpl como parte de nuestro servicio WCF que pueda manejar algo de lógica (quizá validando una llave de aplicación de cualquier forma) y guardar la entidad al almacenamiento de tablas. (Nota: La clase CarDataSource es un simple envoltorio creado para simplificar algo de código, ver la clase entera en la descarga al final de este artículo).

public class CarService : ICarService
{
public void AddCar(Car car)
{
try
{
var key = ValidateApplicationKey();
if (key)
{
var carDataSource = new CarDataSource();
carDataSource.CreateCar(car);
}
}
catch (Exception)
{
throw new WebFaultException<string>(“Failed to create a new car.”, HttpStatusCode.InternalServerError);
}
}

}

Con el servicio en su lugar, podemos llamarlo desde nuestra aplicación Windows Phone así como podríamos llama a cualquier otro servicio web REST. Descarga el paquete completo para ver ese código.

Almacenamiento Blob

Como se mencionó antes, el almacenamiento blob provee un medio para almacenar contenido tales como documentos PDF, imágenes, películas o cualquier otro archivo que quieras tener. Cada archivo es considerado un “blob” y los blobs residen en un contendor. Los contenedores (y además los blobs residentes) están preestablecidamente accesibles solo a aquellos que tengan la llave de acceso al almacenamiento apropiada (la misma llave usada por el almacenamiento de la tabla). Con la llave de acceso, podemos hacer cualquier operación que queramos (leer, crear, borrar, etc.) Es posible cambiar los permisos de un contenedor para permitir el acceso anónimo de lectura, el cual podría ser genial cuando, por ejemplo, necesitamos que todo el mundo vea imágenes desde nuestra aplicación Windows Phone.

¿Qué es lo que vamos a hacer si queremos permitir a alguien tener acceso al contenedor por un periodo de tiempo específico y solo tener un conjunto de permisos durante ese tiempo? La respuesta es usar un Firma de Acceso Compartida (SAS por sus siglas en inglés). Una firma de acceso compartida es una URL especialmente elaborada como cadena de consulta que contiene los permisos de acceso(solo crear, solo borrar, crear y borrar, etc.) y el espacio de tiempo en el cual esos permisos son válidos. Podemos crear una SAS y después dar esta a quien queramos permitirle ese acceso al almacenamiento blob. En nuestro ejemplo, podemos solicitar una Firma de Acceso Compartida desde el servicio WCF y después usar la URL de la SS pra guardar imágenes directamente desde nuestro teléfono en el almacenamiento blob de Windows Azure.

SAS Workflow

El proceso para crear una firma de acceso compartido será como sigue:

public Uri CreateCarImageSharedAccessSignature()
{
Uri signatureUri = null;

var key = ValidateApplicationKey();
if (key)
{
try
{
CloudBlobContainer container = CreateContainer(“cars”);

// Set permissions on the container.
var sas = container.GetSharedAccessSignature(
new SharedAccessPolicy
{
Permissions =
SharedAccessPermissions.Write |
SharedAccessPermissions.List,
SharedAccessStartTime = DateTime.UtcNow,
SharedAccessExpiryTime = DateTime.UtcNow + TimeSpan.FromMinutes(5)
});

// Trim the leading ‘?’ to prevent there from being two in the resulting URI.
var uriBuilder = new UriBuilder(container.Uri) {Query = sas.TrimStart(‘?’)};
signatureUri = uriBuilder.Uri;
}
catch (Exception ex)
{
throw new WebFaultException<string>(ex.Message, HttpStatusCode.InternalServerError);
}
}

return signatureUri;
}

Control de acceso

Ahora que tenemos las bases en posición para acceder a los servicios de almacenamiento de Windows Azure, vamos a agregar una forma de autenticar a lo usuarios de nuestra aplicación. Antes discutimos rápidamente los servicios de control de acceso de Windows Azure (ACS). ACS provee una solución de administración de identidad. Esto nos permite autenticar a los usuarios en Facebook, Yahoo!, Windows Live ID o Google sin tener que escribir el código para cada uno de ellos. Una vez autenticados, vamos a obtener una serie de respuestas que contengan información acerca del usuario (típicamente su nombre y dirección de correo). Podemos entonces usar esa información para personalizar la aplicación o manejar alguna forma de registro de usuarios (preguntar al usuario por mas información). Le elección de cual identidad de proveedor es enteramente nuestra, y es todo hecho vía configuración.

WP7 ACS Control with Yahoo and Google

ACS es un servicio aún simple de usar pero muy poderoso, Para aprender mas de ACS, por favor visita http://www.microsoft.com/windowsazure/learn/control-access/#introductory.

Es muy fácil agregar soporte ACS a una aplicación Windows Phone. Microsoft recientemente lanzó un paquete NuGet que hace el proceso demasiado simple. El paquete NuGet es genial, permitiéndote una solución muy flexible –podemos simplemente elegir agregar ACS a nuestra aplicación y hacerlo de una forma que es fácil a introduce las dependencias mínimas. Puedes encontrar el paquete NuGet buscando por “Access Control Service” en la herramienta de administración de paquetes NuGet en Visual Studio.

ACS NuGet Package in VStudio

O puedes intalar el paquete desde la consola de administración de paquetes NuGet.

PM> Install-Package Phone.Identity.AccessControl.BasePage

De cualquier forma, se ajustarán los controles necesarios en tu aplicación Windows Phone para utilizar ACS. Una vez configurados, podrás seguir las instrucciones dadas para configurar tu aplicación Windows Phone para usar la configuración ACS. Usando el paquete NuGet para agregar soporte ACS a tu aplicación Windows Phone es considerado de alguna forma un proceso avanzado, de esta forma se queda la configuración ACS para nosotros. Hay instrucciones detalladas aquí las cuales proveen una guía detallada paso a paso para crear un nuevo espacio de nombres ACS y cualquier configuración necesaria. La tarea 2 en la guía es probablemente un buen punto de inicio, pero la guía completa es una excelente lectura también.

Push Notifications

Si queríamos, es también muy fácil agregar soporte de notificaciones a nuestra solución. Hay un nuevo paquete NuGet que hace las configuraciones necesarias de una manera muy fácil – muy parecido a lo que vimos al agregar soporte pasa los Servicios de control de acesso. El proceso para hacer esto esta fácilmente demostrado en un video en Channel 9.

Los paquete NuGet para soportar las notificaciones son muy sencillas de hacer también.

PM> Install-Package Phone.Notifications.BasePage (El lado cliente/teléfono se ajusta con el registro del servicio en la nube de notificaciones).

PM> Install-Package CloudServices.Notifications (El lado servidor trabaja con el Servicio de Notificaciones de Microsoft).

Microsoft ha lanzado recientemente bastantes paquetes NuGet que hacen que trabajar con Windows Azure desde nuestro Windows Phone sea sencillo. Hay paquetes para ACS, push notifications, y membresías (los tradicionales nombreUsuarios/Contraseñas). Asegúrate de checar esos paquetes pues te permiten fácilmente mezclar y encontrar los elementos necesarios para tu aplicación.

En resumen

¡Deberíamos estar completos con nuestra aplicación de Windows Phone alimentada por servicios en la nube! Aquí hemos visto cuan fácil puede ser obtener muchos servicios disponibles en la plataforma Windows Azure. Podemos simplemente crear un servicio web que nos dará acceso a nuestra lógica y sirva como puente a los servicios de almacenamiento de Windows Azure. Es también bastante sencillo agregar autenticación de Facebook, Yahoo!, Windows Live o Google a una aplicación existente gracias a los nuevos paquetes NuGet. Descarga el ejemplo completo abajo.

Para descargar una aplicación completa del ejemplo que cubrió a este artículo, puedes hacerlo en el siguiente enlace.

Descarga el código aquí

Para iniciar con Windows Azure, regístrate para una prueba por 90 días.

http://www.microsoft.com/windowsazure/free-trial/.

Recursos

* Windows Azure

* Windows Azure SDK

* Windows Azure Platform Training Kit

* Windows Azure Toolkit para Windows Phone 7

* Paquetes NuGet para Windows Azure y Windows Phone

* Servicios de control de acceso de Windows Azure

* Como crear una cuenta de almacenamiento en Windows Azure

* Escenarios para usar Windows Azure con tus aplicaciones móviles

* Íconos de la arquitectura de Windows Azure cortesía de David Pallman.

Mañana, vamos a discutir el uso de información de ejemplo, y como podemos usar Expression Blend para hacer esto increíblemente fácil para nosotros. ¡Nos vemos!

Categories: MVVM, Tutoriales, WP7 Tags:

MVVM Light Toolkit desde cero (paso 5)

August 2nd, 2011 3 comments

En este quinto artículo de la serie acerca del patrón arquitectural Model-View-ViewModel veremos lo concerniente al “View” ó Vista ó Interfaz de usuario.

El toolkit nos ha creado una clase llamada ViewModelLocator.cs que lo que permite es que la Vista (View.xaml) sepa a cual ViewModel estará enlazada. exploremos un poco esta clase.

En los comentarios iniciales de esta clase se destaca lo siguiente:

Esto nos da una buena pista acerca de lo que el Toolkit provee para agregar ViewModels a este localizador, nos está diciendo que utilicemos el snippet mvvmlocatorproperty y eso es lo que vamos a hacer.

colapsemos todo el código que por omisión se crea, y seguidamente a la declaración de la clase, después de :

vamos a crear una #region Puesto ViewModel y adentro de la región escribimos el snippet mvvmlocatorproperty y la tecla TAB para generar el código contenido en el snippet del toolkit, el cursor se ubica en ViewModelType, reemplazamos por PuestoViewModel que es la case que creamos en el paso 4 luego TAB para que el cursor se ubique en _viewModelPropertyName, reemplazamos por _puestoViewModel y luego TAB para que el cursor se ubique en el comentario /// Gets the ViewModelPropertyName property reemplazamos por PuestoViewModel y luego TAB. Con estos sencillos pasos, hemos creado lo necesario para que el Locator pueda enlazar el ViewModel con la Vista desde el código xaml.

Hay otras formas de enlazar la Vista y el ViewModel que podrían parecer mucho mas sencillas y de hecho lo son, pero el locator nos brindará una gran ayuda cuando estemos elaborando interfaces de usuario mas complejas que requieran la utilización de Microsoft Expression Blend. En la vista de Empleados no utilizaremos el locator para ver otra forma distinta de enlazar estos componentes de nuestra arquitectura.

Como se puede apreciar ya existe un método public static void CleanUp() en el código creado por omisión en el locator, y ahora hemos ingresado otro igual cuando creamos el código para el PuestoViewModel, así que tenemos dos. Copiemos el llamado que se hace en el CleanUp de PuestoViewModel al método ClearPuestoViewModel(); y peguémoslo en el otro método CleanUp seguidamente de ClearMain(); y luego eliminemos el método CleanUp que se generó para PuestoViewModel

  1. public static void Cleanup()
  2.         {
  3.             ClearMain();
  4.             ClearPuestoViewModel();
  5.         }

 

Al final, el código en el locator para localizar el ViewModel de Puesto es el siguiente:

  1. #region Puesto ViewModel
  2.  
  3.         private static PuestoViewModel _puestoViewModel;
  4.  
  5.         /// <summary>
  6.         /// Gets the PuestoViewModel property.
  7.         /// </summary>
  8.         public static PuestoViewModel PuestoViewModelStatic
  9.         {
  10.             get
  11.             {
  12.                 if (_puestoViewModel == null)
  13.                 {
  14.                     CreatePuestoViewModel();
  15.                 }
  16.  
  17.                 return _puestoViewModel;
  18.             }
  19.         }
  20.  
  21.         /// <summary>
  22.         /// Gets the PuestoViewModel property.
  23.         /// </summary>
  24.         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",
  25.             "CA1822:MarkMembersAsStatic",
  26.             Justification = "This non-static member is needed for data binding purposes.")]
  27.         public PuestoViewModel PuestoViewModel
  28.         {
  29.             get
  30.             {
  31.                 return PuestoViewModelStatic;
  32.             }
  33.         }
  34.  
  35.         /// <summary>
  36.         /// Provides a deterministic way to delete the PuestoViewModel property.
  37.         /// </summary>
  38.         public static void ClearPuestoViewModel()
  39.         {
  40.             _puestoViewModel.Cleanup();
  41.             _puestoViewModel = null;
  42.         }
  43.  
  44.         /// <summary>
  45.         /// Provides a deterministic way to create the PuestoViewModel property.
  46.         /// </summary>
  47.         public static void CreatePuestoViewModel()
  48.         {
  49.             if (_puestoViewModel == null)
  50.             {
  51.                 _puestoViewModel = new PuestoViewModel();
  52.             }
  53.         }
  54.  
  55.         #endregion

 

ubicados en el proyecto Empleados.WPF damos clic derecho y seleccionamos Add / New Folder, al folder recién creado la daremos el nombre de “View” ya que aquí es donde residirán todas nuestras vistas ó pantallas. ubicados en el folder View, damos clic derecho y seleccionamos Add / New Item

PuestoView

seleccionamos el tipo MvvmView (WPF) y le damos el nombre PuestoView.xaml a nuestra vista

el código Xaml generado muestra lo siguiente:

  1. <Window x:Class="Empleados.WPF.View.PuestoView"
  2.         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3.         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4.         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  5.         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  6.         mc:Ignorable="d"
  7.         DataContext="{Binding ViewModelName, Source={StaticResource Locator}}">
  8.  
  9.     <Grid>
  10.     </Grid>
  11. </Window>

debemos reemplazar en la propiedad DataContext la palabra ViewModelName, y colocar PuestoViewModel, que lo que hará es buscar una propiedad que se llame así en el locator utilizando un recurso estático que está declarado en el código xaml del archivo App.xaml en donde se le indica cual es la clase “Locator“ en nuestro caso ViewModelLocator. Este es el código de App.xaml

  1. <Application x:Class="Empleados.WPF.App"
  2.              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3.              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4.              xmlns:vm="clr-namespace:Empleados.WPF.ViewModel"
  5.              xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  6.              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  7.              StartupUri="MainWindow.xaml"
  8.              mc:Ignorable="d">
  9.    
  10.     <Application.Resources>
  11.         <!–Global View Model Locator–>
  12.         <vm:ViewModelLocator x:Key="Locator"
  13.                              d:IsDataSource="True" />
  14.     </Application.Resources>
  15.    
  16. </Application>

 

Con estos sencillos pasos ya la Vista tiene conocimiento de cual es su ViewModel y los controles que vayamos a incorporar en nuestra vista podrán enlazarse a las propiedades y comandos que hemos creado en el ViewModel. En modo de diseño crearemos la interfaz que se encargará de dar mantenimiento a puestos, haremos una interfaz muy sencilla ya que el objetivo de los artículos no está centrado en la UI (User Interface) y UX (User Experience), temas que por supuesto cubriré en futuros post con el uso de la herramienta Microsoft Expression Blend, pero por ahora diseñemos algo básico que trabaje con los componentes que hemos venido desarrollando en nuestra arquitectura.

Comencemos por indicar en nuestro código xaml cuales son las dimensiones en modo de diseño con las que queremos trabajar, d:DesignWith=”600” d:DesignHeight=”400” y donde queremos que inicialmente se presente la pantalla WindowStartupLocation=”CenterScreen” además indiquemos las dimensiones de alto y ancho de la pantalla cuando esté en modo de ejecución, With=”600” Height=”400”, además del título de la ventana agregando la propiedad Title=”Gestión de Puestos”, Adicionalmente debemos agregar los siguientes namespaces a nuestro código xaml

xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"

xmlns:cmd="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras.WPF4"

PuestoView1

seleccionamos el Grid en el código xaml, vamos a crear varios rows que nos permitan acomodar los controles con los que vamos a trabajar, una vez seleccionado el Grid en la vista de diseño veremos que alrededor se ve de color celeste, indicando que tenemos seleccionado el Grid, vamos a hacer clic en la barra lateral izquierda, dos veces en distintas ubicaciones de la barra cada vez, con la finalidad de crear rows en nuestro Grid. como se muestra en las siguientes imágenes.

PuestoView1

PuestoView2

ajustamos las dimensiones de altura “Height” en nuestro código xaml para que queden con las dimensiones que se muestra en la imagen, y procedemos a agregar botones, textblocks, textbox, y DataGrid desde el toolbox, como se ve en la imagen a continuación

PuestoView3

Ahora vamos a ir enlazando cada control con su respectiva propiedad o comando del ViewModel, comencemos por los botones.

Seleccionamos el botón Nuevo y en la ventana de propiedades buscamos la propiedad Command y damos clic en Propiedades Avanzadas para acceder al menú contextual y luego seleccionar la opción Apply Data Binding

New

En la ventana siguiente podremos observar las propiedades y comandos que hemos codificado en nuestro ViewModel, recordemos que ya la vista conoce cual es su ViewModel, seleccionamos con doble clic NewCommand en la sección de Path

New2

De la misma forma haremos con los siguientes botones, seleccionando SaveCommand y DeleteCommand en cada caso. Continuamos con el textbox de descripción, de igual forma pero ahora en la propiedad Text del control seleccionamos Propiedades Avanzadas y Apply Data Binding, seleccionamos la propiedad “Descripcion” de nuestro ViewModel y ahí mismo en la ventana de Binding, abrimos la sección de Options y en UpdateSourceTrigger seleccionamos la opción PropertyChanged. Esta opción nos garantiza la correcta comunicación entre el ViewModel y el View, ya sea que se modifique la propiedad en el ViewModel (por que se consultó un puesto ó se limpió la propiedad) ó que se digite información en el textbox, el disparador de actualización será un cambio en la propiedad.

SourceTrigger

Por último seguimos con el DataGrid y en la propiedad ItemsSource seguimos el mismo proceso para aplicar el data binding, en este caso seleccionamos la propiedad de tipo ObservableCollection ListaPuestos y en Options seleccionamos igual que en el caso anterior la opción PropertyChanged en UpdateSourceTrigger, le indicamos al DataGrid que que las columnas serán generadas por nosotros, quitamos el check de AutoGenerateColumns de modo que en el código xaml quede AutoGenerateColumns=”False”.

Vamos a agregar las columnas necesarias así que buscamos la propiedad Columns en la ventana de propiedades y hacemos clic en los puntos suspensivos “…” que indican “Edit items in this collection”, en el Collection Editor:Columns nos aseguramos de que esté seleccionada la opción DataGridTextColumn y damos clic en Add para agregar una nueva columna, en las propiedades de la nueva columna modificamos los valores de IsReadOnly, Header, Width y Binding, y específicamente en esta última propiedad aplicamos el mismo mecanismo que en los anteriores controles para enlazar la propiedad Descripción tal y como se muestra en las siguientes imágenes.

New Column

NewColumn2

Al final damos clic en OK para aceptar los cambios.

Inmediatamente después de la declaración de las columnas en el código xaml y antes de cerrar el DataGrid insertamos el siguiente código:

  1. <i:Interaction.Triggers>
  2.                 <i:EventTrigger
  3.                     EventName="SelectionChanged">
  4.                     <cmd:EventToCommand
  5.                         Command="{Binding SelectionChangedCommand, Mode=OneWay}"
  6.                         CommandParameter="{Binding SelectedItem, ElementName=dataGrid1}" />
  7.                 </i:EventTrigger>
  8.             </i:Interaction.Triggers>

 

Este código es una funcionalidad provista por el Toolkit y lo que permite es capturar un evento determinado e indicar a cual comando en el ViewModel estará enlazado “EventToCommand” pasando por parámetro en este caso el ítem seleccionado “SelectedItem”, para nuestro caso el comando al cual estará enlazado será SelectionChangedCommand, RelayCommand escrito en nuestro ViewModel.

Lo último que tenemos que hacer para desplegar nuestra aplicación es indicar cual será el “View” que se desplegará, y eso lo hacemos modificando el archivo App.xaml en la propiedad StartupUri=”View/PuestoView.xaml”

startupuri

Ejecutamos con F5, La aplicación deberá verse de la siguiente forma:

GestionPuestos

Con esto concluyo el mantenimiento de Puestos, en el siguiente artículo veremos el mantenimiento de Empleados y estaré omitiendo algunos de los pasos que ya hemos visto aquí concentrándome en las particularidades que tenga ese mantenimiento respecto de este que desarrollamos. Gracias y nos vemos en el próximo!. Si desea conocer más acerca del autor de estos artículos puede acceder a su blog personal en la siguiente dirección http://zitrodan.wordpress.com/

MVVM Light Toolkit desde cero (paso 4)

August 2nd, 2011 No comments

Esta es la cuarta entrega de la serie acerca del patrón arquitectural Model – View – ViewModel y específicamente su implementación utilizando MVVM Light Toolkit. En las tres primeras entregas se cubrieron los temas de la instalación, la creación de la base de datos, el modelo utilizando ADO.NET Entity Framework y las clases de lógica de acceso a datos (modelo) utilizando LINQ to Entities. Con el paso 4 pretendo mostrar la implementación del componente “ViewModel” de la arquitectura.

Ubicados en el proyecto Empleados.WPF y en la carpeta ViewModel damos clic derecho y Add New Item…, en el árbol de Installed Templates expandimos WPF y seleccionamos Mvvm, a la derecha se mostrarán las plantillas instaladas por MMVM Light Toolkit. Seleccionamos MvvmViewModel (WPF) y el nombre que le daremos será PuestoViewModel.cs y luego clic en Add.

PuestoViewModelCreation

Esto nos creará una clase que se encargará de interactuar con la Vista (View) ó interfaz de usuario. él código creado por omisión en la clase es el siguiente :

  1. using GalaSoft.MvvmLight;
  2. namespace Empleados.WPF.ViewModel
  3. {
  4.     ///<summary>
  5.     /// This class contains properties that a View can data bind to.
  6.     ///<para>
  7.     /// Use the <strong>mvvminpc</strong> snippet to add bindable properties to this ViewModel.
  8.     ///</para>
  9.     ///<para>
  10.     /// You can also use Blend to data bind with the tool’s support.
  11.     ///</para>
  12.     ///<para>
  13.     /// See http://www.galasoft.ch/mvvm/getstarted
  14.     ///</para>
  15.     ///</summary>
  16.     publicclassPuestoViewModel : ViewModelBase
  17.     {
  18.         ///<summary>
  19.         /// Initializes a new instance of the PuestoViewModel class.
  20.         ///</summary>
  21.         public PuestoViewModel()
  22.         {
  23.             ////if (IsInDesignMode)
  24.             ////{
  25.             ////    // Code runs in Blend –> create design time data.
  26.             ////}
  27.             ////else
  28.             ////{
  29.             ////    // Code runs “for real”: Connect to service, etc…
  30.             ////}
  31.         }
  32.         ////public override void Cleanup()
  33.         ////{
  34.         ////    // Clean own resources if needed
  35.         ////    base.Cleanup();
  36.         ////}
  37.     }
  38. }

Algunos comentarios en el código nos dan la pista de lo que podemos realizar a partir de este momento con la clase PuestoViewModel, el primero es el uso del “Snippet” ó fragmento de código previamente escrito en el Toolkit, “mvvminpc” model-view-viewmodel INotify Property Changed, que nos facilitará la creación de las propiedades que estarán enlazadas (binding) a la vista, luego en el constructor de la clase viene codificado y comentado la sentencia if (IsInDesignMode) que lo que pretende es facilitarnos la codificación si estuviésemos utilizando Microsoft Expression Blend y datos de prueba para ejecutar la aplicación ó si por el contrario estamos utilizando (como es nuestro caso) código real que se conecta a un servicio web ó base de datos para obtener la información.

podemos borrar todos los comentarios por omisión en el código para que nos quede mas limpio y proceder a la creación de las propiedades del ViewModel que estarán enlazadas a la Vista. Inmediatamente después de la declaración de la clase creamos una región donde agruparemos todas las propiedades #region ViewModel Properties, dentro de esta región crearemos regiones para cada una de las propiedades comenzando con #region ViewModel Property : Descripción (la descripción del puesto) dentro de esta primer región escribimos el snippet mvvminpc y luego la tecla TAB para que se genere el fragmento de código correspondiente al snippet. El fragmento de código generado se verá como el siguiente:

  1. #region ViewModel Property : Descripcion
  2.         ///<summary>
  3.         /// The <see cref=“MyProperty” /> property’s name.
  4.         ///</summary>
  5.         publicconststring MyPropertyPropertyName =“MyProperty”;
  6.         privatebool _myProperty =false;
  7.         ///<summary>
  8.         /// Gets the MyProperty property.
  9.         ///TODO Update documentation:
  10.         /// Changes to that property’s value raise the PropertyChanged event.
  11.         /// This property’s value is broadcasted by the Messenger’s default instance when it changes.
  12.         ///</summary>
  13.         publicbool MyProperty
  14.         {
  15.             get
  16.             {
  17.                 return _myProperty;
  18.             }
  19.             set
  20.             {
  21.                 if (_myProperty ==value)
  22.                 {
  23.                     return;
  24.                 }
  25.                 var oldValue = _myProperty;
  26.                 _myProperty =value;
  27.                 // Remove one of the two calls below
  28.                 thrownewNotImplementedException();
  29.                 // Update bindings, no broadcast
  30.                 RaisePropertyChanged(MyPropertyPropertyName);
  31.                 // Update bindings and broadcast change using GalaSoft.MvvmLight.Messenging
  32.                 RaisePropertyChanged(MyPropertyPropertyName, oldValue, value, true);
  33.             }
  34.         }

El cursor se ubicará Inicialmente en MyProperty aquí sobrescribimos reemplazando por la palabra Descripcion que es el nombre de nuestra propiedad y luego damos la tecla TAB para que el cursor se ubique en la siguiente sección del fragmento de código que debemos reemplazar que es el tipo de la propiedad, por omisión el snippet genera de tipo bool, reemplacemos por string, luego TAB para ubicarnos en el atributo _myProperty y modificarlo por _descripcion y nuevamente TAB para ubicarnos en el valor inicial del atributo cuyo valor por omisión es false, lo reemplazamos por string.Empty.

También deberemos eliminar algunas líneas de código que genera el snippet como se ve en la siguiente imagen:

Inpc

el snippet permite implementar cualquiera de dos modalidades para la notificación de cambios, la primera con el comentario // Update bindings, no broadcast es la que utilizaremos por omisión, la segunda opción permite además de notificar cambios de la propiedad a la vista, transmitir su valor mediante Messenging ó mensajería hacía otros ViewModel, por ejemplo podríamos necesitar que un ViewModel transmita el código de puesto hacia otro ViewModel que estuviera enlazado a una vista que nos muestre todos los empleados ligados a ese puesto, como una pantalla de consulta emergente, pero el tema de mensajería será tema de otro post, por el momento eliminemos lo que está marcado en color rojo y de igual forma con cualquier otra propiedad que vayamos a crear.

Con estos sencillos pasos ya tenemos nuestra primer propiedad que implementa la interface INotifyPropertyChanged (heredada de ViewModelBase) y que notificará a nuestro ViewModel de cambios en la Vista y viceversa, notificará a la vista cuando cambiemos el valor de la propiedad. El término notificar no es mas que actualizar la Vista (según el tipo de control que estemos utilizando, Label, TexBox, TextBlock, etc) ó el valor de la propiedad en el ViewModel. La propiedad deberá verse como sigue:

  1. #region ViewModel Property : Descripcion
  2.         ///<summary>
  3.         /// The <see cref=“Descripcion” /> property’s name.
  4.         ///</summary>
  5.         publicconststring DescripcionPropertyName =“Descripcion”;
  6.         privatestring _descripcion =string.Empty;
  7.         publicstring Descripcion
  8.         {
  9.             get
  10.             {
  11.                 return _descripcion;
  12.             }
  13.             set
  14.             {
  15.                 if (_descripcion ==value) return;
  16.                 _descripcion =value;
  17.                 // Update bindings, no broadcast
  18.                 RaisePropertyChanged(DescripcionPropertyName);
  19.             }
  20.         }

Colapsamos la region de la recién creada propiedad y continuamos con la siguiente que será una propiedad que nos permitirá almacenar en memoria el registro completo de algún puesto que sea seleccionado de una lista en la Vista, con lista me refiero a algún DataGrid, ó ListBox donde despleguemos los registros de puestos que se vayan creando ó que sean retornados desde la base de datos.

Para continuar asegurémonos de incluir el siguiente namespace using Empleados.WPF.Model

Creamos una nueva región siempre dentro de la región (#region ViewModel Properties) a la que llamaremos #region ViewModel Property : CurrentPuesto y dentro de esta el snippet mvvminpc para generar el fragmento de código correspondiente; seguimos los mismos pasos como en la propiedad creada anteriormente, al final nuestra propiedad deberá verse como sigue

  1. #region ViewModel Property : CurrentPuesto
  2.         ///<summary>
  3.         /// The <see cref=“CurrentPuesto” /> property’s name.
  4.         ///</summary>
  5.         publicconststring CurrentPuestoPropertyName =“CurrentPuesto”;
  6.         privatePuestoModel _currentPuesto;
  7.         publicPuestoModel CurrentPuesto
  8.         {
  9.             get
  10.             {
  11.                 return _currentPuesto;
  12.             }
  13.             set
  14.             {
  15.                 if (_currentPuesto ==value) return;
  16.                 _currentPuesto =value;
  17.                 // Update bindings, no broadcast
  18.                 RaisePropertyChanged(CurrentPuestoPropertyName);
  19.             }
  20.         }

Para la siguiente propiedad incluimos el namespace using System.Collections.ObjectModel ya que esta propiedad corresponderá a una lista ObservableCollection de objetos de tipo PuestoModel. Este tipo de lista nos permitirá notificar a la Vista cuando un nuevo objeto de tipo PuestoModel sea agregado a la colección. Por ejemplo, si desplegamos los puestos creados en un DataGrid, y este DataGrid está enlazado por medio de Binding a la colección de objetos (ObservableCollection) en el ViewModel, cuando agreguemos un nuevo objeto a la colección el DataGrid se actualizará automáticamente.

La propiedad deberá verse como sigue:

  1. #region ViewModel Property : ListaPuestos
  2.         ///<summary>
  3.         /// The <see cref=“ListaPuestos” /> property’s name.
  4.         ///</summary>
  5.         publicconststring ListaPuestosPropertyName =“ListaPuestos”;
  6.         privateObservableCollection<PuestoModel> _listaPuestos;
  7.         publicObservableCollection<PuestoModel> ListaPuestos
  8.         {
  9.             get
  10.             {
  11.                 return _listaPuestos;
  12.             }
  13.             set
  14.             {
  15.                 if (_listaPuestos ==value) return;
  16.                 _listaPuestos =value;
  17.                 // Update bindings, no broadcast
  18.                 RaisePropertyChanged(ListaPuestosPropertyName);
  19.             }
  20.         }
  21.         #endregion

Con estas tres propiedades concluimos esta parte, continuaremos con la parte que nos permitirá desde la interfaz de usuario (Vista) decirle a nuestro ViewModel que ejecute acciones como Guardar, Eliminar, etc.

Esto se realiza a través de comandos, específicamente a través de la implementación de la Interfaz ICommand, gracias a MVVM Light Toolkit no tenemos que escribir toda la implementación, ya que el Toolkit nos provee de la clase RelayCommand que implementa la interfaz ICommand, esta clase permite definir a través de delegados un método que se llamará cuando se invoque el comando y un método que determinará si el comando se puede ejecutar.

Tenemos que agregar el siguiente namespace using GalaSoft.MvvmLight.Command

Lo siguiente que haremos es crear una nueva región, independiente de la region de las propiedades que se llame #region ViewModel Commands y aquí digitaremos los RelayCommand necesarios tal como sigue:

  1. #region ViewModel Commands
  2.         publicRelayCommand NewCommand { get; set; }
  3.         publicRelayCommand SaveCommand { get; set; }
  4.         publicRelayCommand DeleteCommand { get; set; }
  5.         publicRelayCommand<PuestoModel> SelectionChangedCommand { get; set; }
  6.         #endregion

 

Al primero le he puesto NewCommand, se puede utilizar el nombre que se desee, por ejemplo ComandoLimpiar, etc. public RelayCommand NewCommand {get; set;} es la declaración de un comando que nos permitirá limpiar propiedades (a su valor por omisión) y de forma automática, al estar estas enlazadas (binding) a la vista, limpiar la interfaz de usuario. Este comando se enlazará con un botón en la vista que se encargue de invocarlo.

public RelayCommand SaveCommand {get; set;} es la declaración de un comando que nos permitirá ejecutar la acción de enviar a guardar un nuevo puesto en la base de datos, estará enlazado en la vista por medio de un botón que se encargará de invocarlo

public RelayCommand DeleteCommand {get; set;} es la declaración de un comando que nos permitirá ejecutar la acción de eliminar un puesto de la base de datos

Y por último public RelayCommand<PuestoModel> SelectionChangedCommand {get; set;} es la declaración de un comando que en este caso recibirá por parámetro desde la vista, un objeto de tipo PuestoModel y nos permitirá saber cual es el puesto actualmente seleccionado, por ejemplo desde un DataGrid.

Lo que sigue es la implementación de esos comandos, creamos una region completamente aparte que se llame #region ViewModel Private Methods y adentro escribimos un método que nos permitirá registrar la implementación de los comandos como sigue a continuación:

  1. privatevoid RegisterCommands()
  2.         {
  3.             NewCommand              =newRelayCommand(New);
  4.             SaveCommand             =newRelayCommand(Save, CanSave);
  5.             DeleteCommand           =newRelayCommand(Delete, CanDelete);
  6.             SelectionChangedCommand =newRelayCommand<PuestoModel>(puesto =>
  7.             {
  8.                 if (puesto ==null) return;
  9.                 CurrentPuesto = puesto;
  10.                 Descripcion   = puesto.Descripcion;
  11.             });
  12.         }

Como se puede apreciar nos hace falta la implementación de los métodos New, Save, CanSave, Delete, CanDelete . Lo que haremos a continuación es escribir esos métodos para completar así el registro de los comandos. Creamos una nueva región que llamaremos #region ViewModel Public Methods, ahí deberá estar el constructor de la clase y los métodos anteriormente mencionados que nos permitirán interactuar con la clase Model y ésta a su vez con la base de datos a través de Entity Framework.

  1. #region ViewModel Public Methods
  2.         public PuestoViewModel()
  3.         {
  4.         }
  5.         publicvoid New()
  6.         {
  7.             Descripcion =string.Empty;
  8.             CurrentPuesto =null;
  9.         }
  10.         publicvoid Save()
  11.         {
  12.             if (CurrentPuesto ==null)
  13.             {
  14.                 var puesto =newPuestoModel { Descripcion = Descripcion };
  15.                 puesto.IdPuesto =PuestoModel.InsertPuesto(puesto.Descripcion);
  16.                 ListaPuestos.Add(puesto);
  17.             }
  18.             else
  19.             {
  20.                 CurrentPuesto.Descripcion = Descripcion;
  21.                 PuestoModel.UpdatePuesto(CurrentPuesto);
  22.             }
  23.             New();
  24.         }
  25.         publicbool CanSave()
  26.         {
  27.             return!string.IsNullOrEmpty(Descripcion);
  28.         }
  29.         publicvoid Delete()
  30.         {
  31.             PuestoModel.DeletePuesto(CurrentPuesto.IdPuesto);
  32.             ListaPuestos.Remove(CurrentPuesto);
  33.             New();
  34.         }
  35.         publicbool CanDelete()
  36.         {
  37.             return CurrentPuesto !=null;
  38.         }
  39.         #endregion

Como se puede observar el método New se encargará de limpiar las propiedades que van a interactuar con nuestra vista.

El método Save evalúa si CurrentPuesto es igual a un valor nulo en cuyo caso crea un objeto de tipo PuestoModel e inicializa la propiedad descripción del objeto con la propiedad Descripcion de nuestro ViewModel, luego ejecuta el método estático InsertPuesto y pasa por parámetro la descripción del puesto que queremos insertar y que a su vez retorna el Id entero auto incremental del código de puesto, para finalizar agregando el puesto a la colección de puestos tipo ObservableCollecion ListaPuestos que refrescará automáticamente nuestra Vista, en este caso un Grid con los puestos, si por el contrario CurrentPuesto no es nulo significa que se está en modo de edición de un puesto por consiguiente lo que procede es actualizar la propiedad descripcion del CurrentPuesto con la propiedad Descripción de nuestro ViewModel asumiendo que el usuario la ha modificado en el View, ejecutamos el método PuestoModel.UpdatePuesto pasando por parámetro el objeto PuestoModel que modificamos y finalizamos llamando al método New, para que limpie propiedades y a su vez la Vista.

El método CanSave será el encargado de activar o desactivar el botón “Guardar” en la vista, ya que este retorna un valor booleano dependiendo de si la propiedad Descripción está o no vacía.

Continuamos inspeccionando el método Delete que lo que hace es llamar al método estático DeletePuesto de la clase PuestoModel pasándole por parámetro el Id del puesto, luego elimina ese puesto de la lista de puestos de tipo ObservableCollection y ejecuta el método New.

El método CanDelete activará o desactivará el botón eliminar en la vista dependiendo de si la propiedad CurrentPuesto es null o no.

Una vez implementados los métodos de nuestros RelayCommand, continuamos con el constructor y lo que queremos que se realice cuando la vista “View” se ejecute por primera vez, y esto es, cargar la lista de puestos existentes en la base de datos en nuestra propiedad de tipo ObservableCollection ListaPuestos, además de inicializar nuestros RelayCommands ejecutando el método RegisterCommands.

  1. public PuestoViewModel()
  2.         {
  3.             ListaPuestos =newObservableCollection<PuestoModel>(PuestoModel.GetAllPuestos());
  4.             RegisterCommands();
  5.         }

 

Con esto finalizo el ViewModel de Puesto, lo que sigue es la Vista “View” que veremos en el paso 5 de la serie. Gracias!! Si desea conocer más acerca del autor de estos artículos puede acceder a su blog personal en la siguiente dirección http://zitrodan.wordpress.com/

MVVM Light Toolkit desde cero (paso 3)

August 2nd, 2011 No comments

En este tercer artículo de la serie acerca del patrón arquitectural Model – View – ViewModel voy a profundizar en la implementación del patrón utilizando MVVM Light Toolkit, en el anterior artículo MVVM Light Toolkit desde cero (paso 2) creamos la base de datos, el modelo utilizando ADO .NET Entity Framework y el proyecto WPF.

En el primer artículo MVVM Light Toolkit desde cero (paso 1) se explicó como realizar la instalación del Toolkit.

Existen varias formas de matar las pulgas y no hay recetas escritas en piedra que definan la forma en que se debe trabajar, cada implementación podría tener sus particularidades que ameriten modificaciones a lo que aquí expongo. Por ejemplo, se puede trabajar directamente con las entidades de negocio, en nuestro caso (Empleado y Puesto) accediendo directamente al modelo (Entity Framework) desde el ViewModel ó crear nuestras propias clases POCO (Plain Old CLR Object) y personalizar el desarrollo.

He visto ejemplos donde se accede directamente desde el ViewModel a los objetos del modelo (Entity Framework) viendo la capa, en nuestro caso Empleados.Data como la parte “Model” del Model – View – ViewModel. Lo que sucede en estos casos son varios inconvenientes, desde mi punto de vista no hay una adecuada separación de responsabilidades ya que la responsabilidad principal del ViewModel es interactuar con la Vista (View) enviando y recibiendo información, además, no se estaría encapsulando el código de acceso a datos ya que la teoría dice que un ViewModel debe corresponder a un View y si queremos reutilizar por ejemplo el código que accede a una lista de empleados en otro lugar de nuestro programa deberemos escribir nuevamente el código que nos retorne esa información.

A mi me ha dado buen resultado crear clases para cada entidad de negocio e implementar la interface INotifyPropertyChanged en cada una, así personalizo mejor el desarrollo y a la vez encapsulo el acceso a datos para que sea accedido por cualquier ViewModel que lo necesite, de esta forma estas clases en conjunto con Entity Framework pasarían a ser el componente “Model” de nuestra arquitectura.

Con ese prólogo paso entonces a la creación de las clases que nos permitirán acceder a los objetos del modelo utilizando LINQ y ADO.NET Entity Framework.

Lo primero es crear una clase base y abstracta que nos ayude en la implementación de la interfaz INotifyPropertyChanged hacia todas las clases de lógica de negocio. El enlace de datos o DataBinding es un componente básico del modelo MVVM y como centro del enlace de datos está la interfaz INotifyPropertyChanged, esta interfaz aparentemente simple se utiliza para sincronizar los cambios de propiedad de un objeto con la capa de interfaz de usuario (View).

La interfaz expone un único evento PropertyChanged. Además de especificar que una propiedad individual ha cambiado, el evento también puede indicar que todas las propiedades del objeto han cambiado utilizando una referencia nula o string.empty como el nombre de la propiedad en el PropertyChangedEventArgs.

Ubicados en el proyecto Empleados.WPF y en la carpeta Model hacemos clic derecho y Add Class, a esta clase la llamaremos BaseInpc.cs, agregamos el namespace System.ComponentModel y modificamos para que nuestra clase sea pública y abstracta ya que no queremos que se instancien objetos de esta clase. Seguidamente al nombre le la clase y con dos puntos “:” le decimos que nuestra clase va a implementar la interfaz INotifyPropertyChanged y con clic derecho accedemos al menú contextual y a la opción Implement Interface

BaseInpc_1

Esta acción lo que hará será crear el evento PropertyChanged, vamos a encapsular nuestro código en una región a la que llamaremos #region INotifyPropertyChanged Implementation
a continuación escribiremos el método que nos permitirá pasar por parámetro el nombre de la propiedad para que la interfaz haga su magia

  1. protectedvirtualvoid OnPropertyChanged(string propertyName)
  2.         {
  3.             var handler = PropertyChanged;
  4.             if (handler !=null)
  5.             {
  6.                 handler(this, newPropertyChangedEventArgs(propertyName));
  7.             }
  8.         }

Al final nuestra clase deberá verse de la siguiente forma:

  1. using System.ComponentModel;
  2. namespace Empleados.WPF.Model
  3. {
  4.     publicabstractclassBaseInpc : INotifyPropertyChanged
  5.     {
  6.         #region INotifyPropertyChanged Implementation
  7.         publiceventPropertyChangedEventHandler PropertyChanged;
  8.         protectedvirtualvoid OnPropertyChanged(string propertyName)
  9.         {
  10.             var handler = PropertyChanged;
  11.             if (handler !=null)
  12.             {
  13.                 handler(this, newPropertyChangedEventArgs(propertyName));
  14.             }
  15.         }
  16.         #endregion
  17.     }
  18. }

Lo siguiente es crear nuestras clases de lógica de negocio que encapsularan el acceso a datos, nuevamente ubicados en el proyecto Empleados.WPF y en la carpeta Model hacemos clic derecho y Add Class, la clase la llamaremos PuestoModel.cs ya que será la clase que se encargará de interactuar con objetos de tipo Puesto en nuestro modelo de ADO.NET Entity Framework. Incluimos en la recién creada clase el modificador de acceso “public” , heredamos de BaseInpc, agregamos el namespace Empleados.Data y en una región que llamaremos Model Attributes agregamos la variable que nos permitirá acceder a los objetos de nuestro modelo EmpleadosEntities de ADO.NET Entity Framework la cual llamaremos _context de tipo static. También agregamos un atributo para la descripción del puesto que será de tipo string

  1. using Empleados.Data;
  2. namespace Empleados.WPF.Model
  3. {
  4.     publicclassPuestoModel : BaseInpc
  5.     {
  6.         #region Model Attributes
  7.         privatestaticEmpleadosEntities _context;
  8.         privatestring _descripcion;
  9.         #endregion
  10.     }
  11. }

A continuación crearemos en una nueva región a la que llamaremos #region Model Properties las propiedades de nuestra clase, IdPuesto y Descripcion. Solamente a la propiedad Descripción le implementaremos el método OnPropertyChanged de nuestra clase BaseInpc ya que solamente queremos notificar cambios en esta propiedad.

  1. #region Model Properties
  2.         publicint IdPuesto { get; set; }
  3.         publicstring Descripcion
  4.         {
  5.             get { return _descripcion; }
  6.             set
  7.             {
  8.                 if (_descripcion ==value) return;
  9.                 _descripcion =value;
  10.                 OnPropertyChanged(“Descripcion”);
  11.             }
  12.         }
  13.         #endregion

Ahora vamos con los métodos de nuestra clase, creamos una nueva #region Public Model Methods en donde incluiremos todos los métodos públicos que expondrá nuestra clase.

El primero será el método que permita insertar en la base de datos un nuevo puesto, nuestro código deberá verse de la siguiente forma:

  1. publicstaticint InsertPuesto(string descripcion)
  2.         {
  3.             using (_context =newEmpleadosEntities())
  4.             {
  5.                 var puesto =newPuesto
  6.                                  {
  7.                                      descripcion = descripcion
  8.                                  };
  9.                 _context.AddToPuestos(puesto);
  10.                 _context.SaveChanges();
  11.                 return puesto.IdPuesto;
  12.             }
  13.         }

Aquí pasamos por parámetro (desde el ViewModel) un string con la descripción del puesto e instanciamos nuestro _context dentro de un using con la finalidad de que al cerrarlo se cierre también la conexión con nuestro modelo de datos. Instanciamos e inicializamos con valores un objeto de tipo Puesto y seguidamente utilizamos el método AddToPuestos para que se agregue a la colección de puestos de nuestro modelo y con el método SaveChanges insertamos en la base de datos, para terminar retornando el IdPuesto recién insertado.

Es importante destacar que aquí lo que perseguimos es que nuestros ViewModels no conozcan absolutamente nada acerca de como se interactúa con la base de datos, ni que tipo de base de datos es ó que ORM (Object Relational Mapper) se utiliza, de ahí el porqué, utilizamos la clase PuestoModel para que nuestros ViewModels no tengan que interactuar con objetos de nuestro modelo en ADO.NET Entity Framework.

A continuación el método que nos permitirá modificar la información de un puesto.

  1. publicstaticvoid UpdatePuesto(PuestoModel puestoModel)
  2.         {
  3.             using (_context =newEmpleadosEntities())
  4.             {
  5.                 var puesto = _context.Puestos.FirstOrDefault(p => p.IdPuesto == puestoModel.IdPuesto);
  6.                 if (puesto !=null) puesto.descripcion = puestoModel.Descripcion;
  7.                 _context.SaveChanges();
  8.             }
  9.         }

El siguiente método será el que nos permita eliminar un puesto de la base de datos, pasaremos por parámetro el idPuesto, a continuación el código:

  1. publicstaticvoid DeletePuesto(int idPuesto)
  2.         {
  3.             using (_context =newEmpleadosEntities())
  4.             {
  5.                 var puesto = _context.Puestos.FirstOrDefault(p => p.IdPuesto == idPuesto);
  6.                 _context.DeleteObject(puesto);
  7.                 _context.SaveChanges();
  8.             }
  9.         }

accedemos al puesto utilizando el método FirstOrDefault y una expresión lambda para la comparación de llaves, luego el método DeleteObject y SaveChanges para dar persistencia al cambio en este caso el borrado de información.

El último método que vamos a crear será aquel que nos retorne todos los puestos en la base de datos

  1. publicstaticList<PuestoModel> GetAllPuestos()
  2.         {
  3.             using (_context =newEmpleadosEntities())
  4.             {
  5.                 return _context.Puestos.Select(puesto =>newPuestoModel
  6.                                                     {
  7.                                                         IdPuesto = puesto.IdPuesto,
  8.                                                         Descripcion = puesto.descripcion
  9.                                                     }).ToList();
  10.             }
  11.         }

Aquí retornamos una lista de objetos de tipo PuestoModel, el mismo procedimiento haremos con la entidad Empleado, creando una clase EmpleadoModel que nos permita una correcta asignación de responsabilidades en este caso acceder al modelo ADO.NET Entity Framework utilizando LINQ para las operaciones hacia la base de datos. Pasemos entonces a la creación de de la clase para así ir terminando nuestro “Model” de la arquitectura Model – View – ViewModel.

Ubicados siempre en el proyecto Empleados.WPF y en la carpeta Model, damos clic derecho y Add Class, llamamos a la clase EmpleadoModel.cs le decimos que va a ser public y que heredamos de BaseInpc, creamos una #region Model Attributes e incluimos el atributo public static EmpleadosEntities _context y los demás atributos de la entidad Empleado como se ve en la imagen.

  1. using Empleados.Data;
  2. namespace Empleados.WPF.Model
  3. {
  4.     publicclassEmpleadoModel : BaseInpc
  5.     {
  6.         #region Model Attributes
  7.         privatestaticEmpleadosEntities _context;
  8.         privatestring _identificacion;
  9.         privatestring _nombre;
  10.         privatestring _primerApellido;
  11.         privatestring _segundoApellido;
  12.         privatestring _genero;
  13.         #endregion
  14.     }
  15. }

Seguimos con las propiedades de la clase

  1. #region Model Properties
  2.         publicint IdEmpleado { get; set; }
  3.         publicstring Identificacion
  4.         {
  5.             get { return _identificacion; }
  6.             set
  7.             {
  8.                 if(_identificacion ==value) return;
  9.                 _identificacion =value;
  10.                 OnPropertyChanged(“Identificacion”);
  11.             }
  12.         }
  13.         publicstring Nombre
  14.         {
  15.             get { return _nombre; }
  16.             set
  17.             {
  18.                 if (_nombre ==value) return;
  19.                 _nombre =value;
  20.                 OnPropertyChanged(“Nombre”);
  21.             }
  22.         }
  23.         publicstring PrimerApellido
  24.         {
  25.             get { return _primerApellido; }
  26.             set
  27.             {
  28.                 if (_primerApellido ==value) return;
  29.                 _primerApellido =value;
  30.                 OnPropertyChanged(“PrimerApellido”);
  31.             }
  32.         }
  33.         publicstring SegundoApellido
  34.         {
  35.             get { return _segundoApellido; }
  36.             set
  37.             {
  38.                 if (_segundoApellido ==value) return;
  39.                 _segundoApellido =value;
  40.                 OnPropertyChanged(“SegundoApellido”);
  41.             }
  42.         }
  43.         publicstring Genero
  44.         {
  45.             get { return _genero; }
  46.             set
  47.             {
  48.                 if (_genero ==value) return;
  49.                 _genero =value;
  50.                 OnPropertyChanged(“Genero”);
  51.             }
  52.         }
  53.         publicstring NombreCompleto
  54.         {
  55.             get { returnstring.Format(“{0} {1} {2}”, Nombre, PrimerApellido, SegundoApellido); }
  56.         }
  57.         publicPuestoModel Puesto { get; set; }

Una vez concluidas las propiedades pasamos a escribir los métodos para lo cual creamos una #region Public Model Methods e incluimos el método que nos permita Insertar en la base de datos un nuevo empleado

  1. publicstaticint InsertEmpleado(EmpleadoModel empleadoModel)
  2.         {
  3.             using (_context =newEmpleadosEntities())
  4.             {
  5.                 var empleado =newEmpleado
  6.                                    {
  7.                                        identificacion  = empleadoModel.Identificacion,
  8.                                        nombre          = empleadoModel.Nombre,
  9.                                        primerApellido  = empleadoModel.PrimerApellido,
  10.                                        segundoApellido = empleadoModel.SegundoApellido,
  11.                                        genero          = empleadoModel.Genero,
  12.                                        IdPuesto        = empleadoModel.Puesto.IdPuesto
  13.                                    };
  14.                 _context.AddToEmpleados(empleado);
  15.                 _context.SaveChanges();
  16.                 return empleado.IdEmpleado;
  17.             }
  18.         }

Continuamos con el método Update de un empleado.

  1. publicstaticvoid UpdateEmpleado(EmpleadoModel empleadoModel)
  2.         {
  3.             using (_context =newEmpleadosEntities())
  4.             {
  5.                 var empleado = (from e in _context.Empleados
  6.                                 where e.IdEmpleado == empleadoModel.IdEmpleado
  7.                                 select e).First();
  8.                 empleado.identificacion  = empleadoModel.Identificacion;
  9.                 empleado.nombre          = empleadoModel.Nombre;
  10.                 empleado.primerApellido  = empleadoModel.PrimerApellido;
  11.                 empleado.segundoApellido = empleadoModel.SegundoApellido;
  12.                 empleado.genero          = empleadoModel.Genero;
  13.                 empleado.IdPuesto        = empleadoModel.Puesto.IdPuesto;
  14.                 _context.SaveChanges();
  15.             }
  16.         }

En este caso he realizado una modificación en la forma de acceder al registro del empleado que queremos modificar, estoy utilizando LINQ to Entities en lugar de una expresión lambda, para que se observe también las distintas formas de trabajar con el acceso de datos en ADO.NET Entity Framework.

Seguimos con el método para eliminar un empleado

  1. publicstaticvoid DeleteEmpleado(int idEmpleado)
  2.         {
  3.             using (_context =newEmpleadosEntities())
  4.             {
  5.                 var empleado = (from e in _context.Empleados
  6.                                 where e.IdEmpleado == idEmpleado
  7.                                 select e).First();
  8.                 _context.DeleteObject(empleado);
  9.                 _context.SaveChanges();
  10.             }
  11.         }

y por último el método que retorna todos los empleados.

  1. publicstaticList<EmpleadoModel> GetAllEmpleados()
  2.         {
  3.             using (_context =newEmpleadosEntities())
  4.             {
  5.                 return _context.Empleados.Select(empleado =>
  6.                     newEmpleadoModel
  7.                         {
  8.                             IdEmpleado      = empleado.IdEmpleado,
  9.                             Identificacion  = empleado.identificacion,
  10.                             Nombre          = empleado.nombre,
  11.                             PrimerApellido  = empleado.primerApellido,
  12.                             SegundoApellido = empleado.segundoApellido,
  13.                             Genero          = empleado.genero,
  14.                             Puesto          =newPuestoModel
  15.                             {
  16.                                 IdPuesto    = empleado.Puesto.IdPuesto,
  17.                                 Descripcion = empleado.Puesto.descripcion
  18.                             }
  19.                         }).ToList();
  20.             }
  21.         }

Con esto doy por concluido el tercer artículo de la serie y cubierto el tema acerca de la parte “Model” de nuestra arquitectura Model – View – ViewModel . Podríamos escribir los test necesarios para probar nuestros métodos y en un ambiente de desarrollo normal es lo correcto, pero se sale un poco del alcance del artículo, ya que mi objetivo es mostrar el uso del patrón como tal y específicamente el MVVM Light Toolkit. Muy probablemente estaré incluyendo esos test en la versión final del código fuente. En el siguiente artículo estaré dando cobertura al ViewModel. Si desea conocer más acerca del autor de estos artículos puede acceder a su blog personal en la siguiente dirección http://zitrodan.wordpress.com/

MVVM Light Toolkit desde cero (paso 2)

August 2nd, 2011 1 comment

Este es el segundo artículo de una serie en la que estaré explicando como implementar el patrón Model – View – ViewModel. En el primer artículo se explicó como realizar la instalación de MVVM Light Toolkit.

Este segundo artículo tratará acerca de como utilizar el patrón en una arquitectura con ADO.NET Entity Framework comenzando por la creación de la base de datos, su implementación en un proyecto Visual Studio .NET 2010 y el esqueleto de arquitectura de nuestro proyecto de ejemplo.

Para este caso estoy utilizando SQL Server 2008 R2 y lo que voy a crear es una base de datos de empleados, así que comencemos por acceder a SQL Server Management Studio y creamos una nueva base de datos con el nombre Empleados, luego creamos una nueva tabla con las siguientes columnas:

Puesto

Esta tabla será la encargada de almacenar la información de puestos de trabajo, Indicamos que la llave primaria será el IdPuesto y que será un campo de tipo entero auto incremental, guardamos la tabla con el nombre Puesto.

Seguidamente creamos una nueva tabla con las siguientes columnas:

Empleado

Esta tabla será la encargada de almacenar la información relativa a empleados, de igual forma indicamos que la llave primaria será IdEmpleado y que será un campo de tipo entero auto incremental, guardamos la tabla con el nombre Empleado.

Establecemos la relación entre la tabla Puesto y la Tabla Empleado de la siguiente forma, ubicados en la tabla Empleado presionamos el botón “Relationships”, eso nos permite acceder al cuadro de diálogo para establecer la relación.

Relacion1

Relacion2

Cerramos el cuadro de diálogo y guardamos. No voy a ahondar mucho en el tema de la base de datos y algunos otros controles que deberíamos implementar para garantizar la consistencia de la información, como por ejemplo crear índices de tipo “Unique” para el campo de descripción del Puesto, igualmente para algunos otros campos de la tabla Empleado, ya que el objetivo del artículo es concentrarnos en la implementación del patrón arquitectural Model – View – View Model

Con estas dos tablas ya podemos comenzar a crear nuestro proyecto de ejemplo utilizando ADO.NET Entity Framework.

Accedemos a Visual Studio 2010 y creamos un nuevo proyecto de tipo Librería de Clases, al proyecto le daremos el nombre Empleados.Data y nuestro Solution se llamará MVVMEjemplo como se ve en la siguiente imagen.

NewProject

Eliminamos la clase que se crea por omisión y ubicados en el Solution Explorer y en nuestro proyecto Empleados.Data damos clic derecho para agregar un nuevo ítem de tipo ADO.NET Entity Data Model al que llamaremos EmpleadosModel.edmx y presionamos Add para crear nuestro modelo.

CreateModel

En el siguiente cuadro de diálogo seleccionamos generar nuestro modelo a partir de una base de datos y luego Next, en el siguiente cuadro de dialogo seleccionamos New Connection y establecemos la conexión con la base de datos Empleados.

NewConnection

DataConnection

Presionamos Next y en el siguiente cuadro de diálogo nos aseguramos de que las opciones de Pluralizar o Singularizar los nombres de los objetos generados esté marcado y la opción de incluir columnas de llaves foráneas en el modelo también, así como las tablas a partir de las cuales queremos generar nuestro modelo. Dejamos el nombre por omisión del Model Namespace como EmpleadosModel y luego presionamos Finish.

DataObjects

Model

Listo, hemos creado el modelo a partir de la base de datos, vamos a realizar algunas modificaciones a los nombres de objetos que Entity Framework a inferido utilizando la pluralización, el primero será a la propiedad de navegación en la tabla Puesto que por omisión tiene el nombre Empleadoes y no queremos que se llame así, la modificamos en la ventana de propiedades a Empleados.

Navigation

Luego seleccionamos la tabla Puesto en el modelo y en la ventana de propiedades modificamos el Entity Set Name, lo cambiamos a Puestos

Puestos

Igual hacemos con la tabla Empleado, la seleccionamos en el modelo y en la ventana de propiedades modificamos el Entity Set Name a Empleados

Empleados

Ahora vamos a crear nuestro proyecto de Windows Presentation Foundation que será la base de nuestra implementación del patrón arquitectural.

Ubicados en el Solution Explorer y en el Solution, damos clic derecho y seleccionamos Add New Project, el proyecto que vamos a crear será de tipo MvvmLight (WPF4) y lo llamaremos Empleados.WPF

NewWPFProject

Una vez creado el nuevo proyecto vamos a agregar las siguientes referencias que nos permitirán acceder a nuestro modelo, ubicados en References de nuestro proyecto Empleados.WPF damos clic derecho y Add Reference

La primer referencia será a System.Data.Entity

System.Data.Entity

La siguiente referencia será al proyecto Empleados.Data

Empleados.Data

Lo siguiente que haremos será copiar el App.Config de nuestro proyecto Empleados.Data y lo pegamos en el proyecto Empleados.WPF ya que este proyecto necesitará conocer la cadena de conexión para acceder a la base de datos, luego colocamos este proyecto como el proyecto de inicio.

StartUpProject

Ejecutamos el Proyecto presionando F5.

Main

Deberemos ver la siguiente pantalla indicándonos que todo está funcionando correctamente.

Para conocer mas acerca de ADO.NET Entity Framework recomiendo leer el libro de Julia Lerman

Programming Entity Framework: Building Data Centric Apps with the ADO.NET Entity Framework

En el siguiente artículo MVVM Light Toolkit desde cero (paso 3) continuaremos la construcción de nuestra aplicación. Si desea conocer más acerca del autor de estos artículos puede acceder al blog personal en la siguiente dirección http://zitrodan.wordpress.com/

MVVM Light Toolkit desde cero (paso 1)

August 2nd, 2011 3 comments

En este artículo voy a comentar acerca de cómo implementar el patrón Model – View – View Model utilizando MVVM Light Toolkit. Iniciaremos con el proceso de instalación desde cero, utilizando Visual Studio 2010 y luego en los siguientes artículos iremos desarrollando una aplicación de ejemplo utilizando una arquitectura MVVM sobre la base de ADO.NET Entity Framework, LINQ to Entities y Windows Presentation Foundation.

Si bien nos encontraremos muchos artículos que hablan acerca del patrón y de MVVM Light Toolkit, son pocos los que brindan ejemplos detallados en una arquitectura de aplicaciones del tipo LOB (Line of Business) y el uso en conjunto con Entity Framework.

Lo primero es conocer de qué se trata el patrón arquitectural y como nos facilita la construcción de nuestras aplicaciones. MVVM es en realidad un patrón que se construye en WPF y Silverlight y la idea principal es la separación del diseño o capa de presentación de la lógica de negocio y los datos; la capa de presentación será lo que se conocerá como (View), la lógica de negocio será el (ViewModel) y la data será el (Model). Si desea profundizar en el patrón como tal y conocer sus orígenes le recomiendo los siguientes enlaces a artículos y videos:

http://maromasdigitales.net/2010/05/patron-mvvm-explicado/

http://channel9.msdn.com/Events/PDC/PDC10/CD50

http://msdn.microsoft.com/en-us/magazine/dd419663.aspx

MVVM Light Toolkit

Obtenemos primero que todo los archivos necesarios para iniciar el proceso de instalación desde el sitio de Codeplex http://mvvmlight.codeplex.com/ A la fecha en que este artículo está siendo escrito la versión de MVVM Light Toolkit es la versión 3 Service Pack 1, así que el archivo que será descargado al computador será GalaSoft.MvvmLight.V3SP1WithHotfix.zip, se descomprime el archivo en una carpeta, una vez realizado esto el resultado serán los siguientes archivos:

  • GalaSoft.MvvmLight.Binaries.V3.zip
  • GalaSoft.MvvmLight.Snippets.V3.zip
  • GalaSoft.MvvmLight.Templates.V3.Blend3.zip
  • GalaSoft.MvvmLight.Templates.V3.Blend4.zip
  • GalaSoft.MvvmLight.Templates.V3.VS08.zip
  • GalaSoft.MvvmLight.Templates.V3.VS10.zip
  • GalaSoft.MvvmLight.Templates.V3.VS10X.zip

    Los archivos incluidos sirven para varios propósitos, entre ellos la utilización con Microsoft Expression Blend y distintas versiones de Visual Studio. Como nota importante al descomprimir los archivos en Windows Vista o Windows 7 estos quedan marcados como “no seguros” por el sistema operativo, se debe eliminar este bloqueo antes de descomprimirlos.

  • Seleccione el archivo zip
  • Clic derecho sobre el archivo y seleccione propiedades en el menú contextual
  • En el cuadro de diálogo de propiedades en la pestaña General, clic en el botón Unblock

    Unblock

    Instalación de los binaries

    Seleccione el archivo zip GalaSoft.MvvmLight.Binaries.V3.zip , clic derecho y la opción Extraer Todo, cambie la ruta donde los archivos serán extraídos  y coloque C:\ y a continuación confirme que si desea combinar los archivos dentro de la carpeta Program Files, Los archivos deberán quedar en la siguiente carpeta:

    C:\Program Files\Laurent Bugnion (GalaSoft)\Mvvm Light Toolkit\Binaries

    Instalación de templates para Visual Studio 2010

    En Visual Studio revise la ubicación desde donde los templates son cargados: Tools/ Options /Projects and Solutions

    ProjectTemplates

    copie la ruta sin la carpeta ProjectTemplates, por ejemplo en mi caso sería :

    C:\Users\Dan\Documents\Visual Studio 2010\Templates\

    A continuación seleccionemos el archivo zip GalaSoft.MvvmLight.Templates.V3.VS10.zip , clic derecho y la opción Extraer Todo, en el cuadro de dialogo pegue la ruta copiada anteriormente y confirme la extracción.

    Instalación de Snippets para Visual Studio 2010

    En Visual Studio revise la ubicación desde donde los snippets son cargados: Tools/ Code Snippets Manager / My Code Snippets

    Snippets

    copie la ruta en “Location:”, por ejemplo en mi caso sería :

    C:\Users\Dan\Documents\Visual Studio 2010\Code Snippets\Visual C#\My Code Snippets

    A continuación seleccionemos el archivo zip GalaSoft.MvvmLight.Snippets.V3.zip , clic derecho y la opción Extraer Todo, en el cuadro de dialogo pegue la ruta copiada anteriormente y confirme la extracción.

    Verificando la instalación

    Inicie Visual Studio

    Seleccione File / New Project

    Se deben visualizar los templates de proyectos MVVM Light bajo Visual C#

    Cree una nueva aplicación de tipo MVVMLight (WPF4)

    Abra la carpeta References en el Solution Explorer

    Asegúrese de que los archivos Dll GalaSoft.MvvmLight.WPF4, GalaSoft.MvvmLight.Extras.WPF4 y System.Windows.Interactivity están correctamente referenciados (sin un símbolo de advertencia)

    Verifying

    Más acerca de MVVM Light Toolkit puede ser accedido desde el sitio web de Galasoft http://www.galasoft.ch/mvvm/ y leyendo el blog de su creador, Laurent Bugnion http://blog.galasoft.ch/

    En el siguiente artículo MVVM Light Toolkit desde cero (paso 2) comenzaremos la construcción de nuestra aplicación. Si desea conocer más acerca del autor de estos artículos puede acceder a mi blog personal en la siguiente dirección http://zitrodan.wordpress.com/

  • Retrospectiva sobre MEF: usos en el mundo real

    April 11th, 2011 No comments

    Evaluación de diferentes formas en las que el autor ha usado MEF para mejorar la arquitectura de varias aplicaciones de negocios a gran escala en las que ha estado envuelto. Desde el manejo de valores de configuración, hasta la administración de vistas y regiones modulares, pasando por mensajería y uso de comandos, Jeremy Likness ha usado MEF para crear aplicaciones flexibles y extensibles en Silverlight.

     

    Este artículo fue publicado originalmente en Maromas Digitales y es una traducción aproximada del artículo MEF in the Wild: A Retrospective publicado en inglés por Jeremy Likness el 27 de setiembre del 2010 en su blog C#er:Image.

     

    Luego de haber trabajado en varios proyectos grandes usando la Infraestructura de extensibilidad administrada, me parece buena idea compartir mis experiencias en cómo lo usamos y las ventajas que nos dio. Las siguientes son diferentes formas en las que MEF nos ayudó a crear aplicaciones modulares en Silverlight .

    A diferencia de mi artículo anterior titulado Diez razones para usar MEF, este es más un catálogo de casos específicos usando MEF en proyectos reales.

     

    Inversión de control e inyección de dependencias

    Esta fue probablemente la decisión más fácil. Ninguno de los proyectos necesitaba un mecanismo de inversión de control (IoC) o inyección de dependencias más sofisticado que el ofrecido por MEF. Ya que de todos modos íbamos a usarlo por otras razones, y además viene incluido en la plataforma .NET, nos pareció lógico usarlo para resolver este problema. En la mayoría de los casos bastó con simplemente declarar una interfase, exportarla donde es implementada e importarla donde fuese necesario. Describo a continuación otros casos más específicos.

     

    Configuración

    Para nosotros fue muy importante el manejo de los datos de configuración y MEF nos hizo la tarea fácil. La razón por la que lo menciono en esta categoría es por ser, en realidad, un efecto secundario de la inversión de control. Mediante definir una interfase es posible que sea usada por el resto de código; además, en la etapa de prueba los valores de configuración pueden ser fácilmente simulados. El servicio principal de la aplicación implementa la configuración y usa una combinación de constantes definidas durante la compilación y otros parámetros para cargar los valores. Los consumidores en cualquier parte de la aplicación, incluso si son módulos cargados de forma dinámica y sin tener referencias concretas a al ensamblaje donde reside el mecanismo de configuración, tan sólo necesitan importar la interfase para tener acceso a los valores.

     

    Registros de historial

    El patrón IoC implementado en MEF también nos ayudó de forma indirecta a implementar un registro de historial. Lo que hicimos fue crear una interfase implementada por un registro de historial y acostumbrarnos a usarlo en vez de Debug.WriteLine, con la ventaja de poder indicar el grado de severidad, la fuente del evento y otros datos auxiliares. Al separar las funciones de este modo pudimos usar al principio un mecanismo simple de registro (por ejemplo, escribir en la ventana de depuración) con la flexibilidad de poder añadir más adelante otros mecanismos como almacenaje aislado, llamadas a otros servicios, y así por el estilo. Así simplificamos el manejo de transacciones desde un punto central sin tener que alterar el resto de la aplicación.

     

    Administración de regiones

    Antes que nada quiero aclarar que Prism 4.0 posee una excelente infraestructura para el manejo de regiones y no tengo nada en su contra ni es mi intención reinventar algo ya existente. Lo que pasó es que nuestro proyecto tenía sólo unas pocas regiones especificadas por lo que era mejor usar algo más liviano.

    El manejo de regiones se compone esencialmente de unos cuantos componentes. Tenemos una interfase que sirve como adaptador para las regiones y que admite clases de tipo contenedor (como Grid o ContentControl y demás). Para crear nuevos adaptadores basta con heredar la interfase, agregar la información adicional de exportación para describirle a MEF el contenedor con el que es compatible, y luego crear eventos como el registro (apuntar la vista a una región), activación (hacer que la vista aparezca en la región) y desactivación (remover la vista). A veces tuvimos que manipular el contenido, o algunos de los elementos secundarios, pero en general el administrador de estados visuales (Visual State Manager) se encarga de esa tarea.

    El contenedor es definido como una región usando una propiedad adjunta y cada vista contiene metainformación describiendo a cuál región ha de ser asignada. El administrador de regiones agrupa y coordina todos estos componentes. Si necesito activar una vista, el administrador de regiones usa el patrón de cadena de responsabilidades para encontrar el adaptador de la región a la que pertenece la vista y luego le pide que la despliegue.

     

     

    El artículo Usando MEF en vez de Prism – Parte 2 da una explicación detallada sobre el manejo de regiones. También pueden ver el artículo en inglés Advanced Silverlight Applications using MEF para una infraestructura de muestra.

     

    Integración de servicios

    Es común que en aplicaciones empresariales hayan diferentes modelos u objetos del dominio del problema a resolver que comparten operaciones de datos. Por ejemplo, su creación, actualización, borrado y consulta. Por esta razón, pude crear una interfase abstracta IServiceFor<T> que define operaciones de este tipo. Las implementaciones de la interfase son exportadas de manera explícita y el direccionador de servicios se encarga de asignarlas a las entidades correspondientes.

    Esto quiere decir que puedo crear un modelo de vista para una entidad, y simplemente solicitar al direccionador de servicios que me provea un servicio que ofrezca las operaciones genéricas, sin tener que preocuparme de cómo fue implementado el servicio. Se hace fácil entonces simular los servicios mientras se desarrollan las operaciones en sí. Por ejemplo, algunos servicios usaron el almacenamiento aislado mientras que otros enviaban mensajes por la red, pero a fin todos fueron desacoplados del modelo de vista y agrupados por MEF.

     

     

    Mensajería

    Otro aspecto importantísimo en cualquier sistema es la mensajería. Por ejemplo, imaginen que tenemos un modelo de vista que ofrece filtrado de datos y que puede ser usado por otros modelos de vista. ¿Cómo pueden enterarse estos otros modelos de vista cuando cambia la especificación del filtro, o cómo pueden obtener el filtro en sí?

    En este proyecto lo hicimos de dos formas.

     

    Composición de vistas

    Aunque sea aplicado de manera global, un filtro puede ser considerado como parte del modelo de vista que lo use. Si bien por sí mismo no es de utilidad, al añadirlo como un componente de otro modelo de vista puede entonces ser usado en ese contexto. MEF fue muy útil para aplicar este patrón ya que cualquier modelo de vista que necesite usar otro no más necesita importarlo:

     

     

    Por supuesto, todavía no podemos saber cuando el filtro ha cambiado a menos que nos suscribamos al evento de propiedad cambiada. Puede que eso sea razonable algunas veces, pero hay otras en que un mensaje de ese tipo es lo suficientemente importante como para que varios componentes necesiten escucharlos. Para ese fin usamos el patrón agrupador de eventos.

     

    Agrupador de eventos (EventAggregator)

    Mi versión del agrupador de eventos se basó en un artículo que describe una implementación basada en extensiones reactivas (RX). Le hice algunas modificaciones para que fuera compatible con Silverlight y trabajó de maravilla.

    Otro caso común fue usar mensajes para causar cambios en la aplicación. Por ejemplo, para avisar sobre un nuevo filtro o una nueva selección por parte del usuario. Para eso hice una clase dedicada a difundirlos, llevando cuenta del tipo, el mensaje y una enumeración. De esa manera, si quiero anunciar una nueva selección entonces puedo crear una instancia de esta clase e insertar el elemento elegido junto con su tipo y usar la enumeración para indicar que es “select” para luego publicar el evento.

    Quien esté interesado en este tipo de mensaje puede entonces escucharlo de esta manera:

     

     

     

    Direccionamiento XAPs modulares

    El hecho de que las aplicaciones de Silverlight son transferidas a través de la red y almacenadas en la máquina del usuario hacen que el cargado dinámico de módulos XAP sea de gran importancia. Hay dos razones principales: el tiempo de descarga y la cantidad de memoria requerida en el cliente. Mediante usar cargado dinámico, podemos fragmentar la aplicación en módulos XAP y diferir su uso hasta que sean necesarios. Como resultado, se reduce el tiempo inicial de carga de la aplicación y la memoria requerida al principio.

    Un reto que experimentan algunas bibliotecas es cuándo y cómo iniciar el cargado del módulo. Por mi parte, yo prefiero un método al que he llamado “direccionamiento dinámico de XAPs.” Es una idea sencilla: asignar etiquetas a las vistas, ya sea usando nombres explicativos o el espacio de nombres completo.  En mi proyecto principal, donde radica la infraestructura, tengo un conjunto de constantes globales que representan las diferentes vistas.  Aunque el módulo no sabe nada sobre la vista, las constantes sirven para referirse a ella. Otro conjunto de constantes describen los archivos XAP disponibles.

    Con estas dos pistas puedo entonces usar un servicio de inicialización que encamine de manera dinámica los módulos usando catálogos de distribución para cargarlos. Una ruta de direccionamiento apunta hacia la vista y el archivo XAP que la contiene y es exportada para uso de cualquier componente que necesite cargarla. Cuando se solicita una vista, el administrador de regiones verifica que exista la ruta correspondiente y luego le pide al servicio de direccionamiento que cargue el archivo XAP, demorando la navegación a la vista hasta que haya sido cargada. Esto hace fácil separar la navegación de los módulos, haciendo posible que cualquier área de la aplicación solicite vistas, mientras que la infraestructura se preocupa de cargar los módulos necesarios y proveer las rutas requeridas.

     

    Direccionamiento de modelos de vista

    Un problema común en MVVM es cómo atar el modelo de vista a la vista. Ninguna de las bibliotecas que he usado provee correlaciones uno-a-uno entre ambos debido a que a veces un modelo de vista sirve a varias vistas. Aunque las vistas dependen de él (se le puede considerar como el contrato de ligado de la vista), el modelo de vista no sabe nada sobre las vistas a las que está asociado. Las vistas son exportadas a la región de destino usando su tipo o con una etiqueta, de igual manera que los modelos de vista son exportados con una etiqueta.

    Las rutas son implementadas con un simple objeto que contiene dos etiquetas, una para la vista y otra para el modelo de vista. Ya sea que la vista exporte la etiqueta o que lo haga una clase para direccionamiento, la exportación es al final manejada por un direccionador centralizado. Este direccionador es capaz de extraer vistas y, como todo es cargado de manera diferida, puede determinar si la vista necesita ser atada al modelo de vista. Por otra parte, también provee una referencial al modelo de vista que, por cierto, es creado si es la primera vez que se usa.

    El resultado es que la asociación entre la vista y el modelo de vista es completamente desacoplada, y es descrita en otra parte de la aplicación. También significa que el sistema de enrutamiento conoce cuando una vista está siendo desplegada por primera vez de manera que puede llamar métodos de inicialización en el modelo de vista tanto al ser creada como cuando es activada posteriormente. Esto permite que mis vistas reaccionen a cambios o mantenimiento que sea necesario antes de ser desplegadas de nuevo sin tener que depender de la jerarquía visual.

     

     

    Para más detalles pueden ver los artículos Atando el modelo de vista con MEF (en inglés) y Otro patrón de localización de modelos de vista.

     

    Desacoplando las vistas

    Hay situaciones en las que es necesario hacer referencia a un tipo específico de vista que no es conocida de antemano. Por ejemplo, puede que una infraestructura de navegación dependa del tipo de vista (como en la sección siguiente). Cuando la vista se encuentra en un XAP que todavía no ha sido cargado, no hay manera de obtener una referencia directa a la vista. En este caso, MEF fue muy útil para manipular tipos diferidos, es decir, es posible importar una propiedad de un tipo especifico usando tan sólo una etiqueta (como “MainView” o “DashboardView”) y exportar esa etiqueta en otra parte. Así pudimos separar completamente la interacción con la vista y su tipo.

     

    Navegación

    MEF provee una infraestructura de navegación muy flexible. Lo que yo quería es un tipo de navegación capaz de entender entidades compuestas, de manera que un evento de navegación no estuviese limitado a páginas en la aplicación sino más bien a vistas, incluso cuando hay vistas dentro de otras.

    La navegación coopera con el direccionador de modelos de vista para estar al tanto de cambios en estos últimos. En la infraestructura que desarrollé, el agrupador de eventos basado en MEF simplemente genera un evento de navegación de vista que encapsula el tipo de la vista y un indicador de activación o desactivación. El direccionador, que ha importado todas las vistas y modelos de vista, encuentra la vista y se comunica con el administrador de regiones para activarla o desactivarla. Si es activada entonces también llama el método correspondiente de inicialización en el modelo de vista.

    Al generalizar cuadros de diálogo, páginas, y hasta asistentes (Wizards) dentro del concepto de eventos navegación, la tarea se hace increíblemente simple. En este contexto, el que una ventana sea emergente es tan sólo un detalle de implementación independiente del evento en sí, y el modelo de vista simplemente solicita que se navegue a esa vista sin tener que preocuparse de modelos, regiones, o ningún otro detalle propio de la navegación.

     

    Autorización

    La mayoría de las aplicaciones de negocios requieren servicios de autenticación y autorización. Por lo general, el usuario tiene propiedades como roles y credenciales que son verificados a la hora de obtener acceso a diferentes partes de la aplicación. MEF hace fácil el difundir las credenciales ya que el el usuario puede ser exportado como un objeto concreto, o como una interfase, y luego importado o consultado por las áreas de la aplicación que requieran saber el nivel de autorización.

     

    Comandos

    Los comandos pueden ir desde los que están fuertemente entrelazados con la interfase al usuario, hasta los que son tan independientes que pueden residir en módulos aún no cargados. Por ejemplo, comandos como volver al inicio (“home”) o expandir requieren eventos de navegación que conozcan la vista de destino.

     

    Comandos de enlace diferido

    El mecanismo de exportación e importación de MEF nos ayudó a implementar este tipo de enlace, también conocido como enlace tardío. En algunos casos el modelo de vista importa el comando usando una etiqueta predeterminada (por ejemplo “home”) aun si la vista se encuentra en un módulo diferente. Cuando es cargado, el módulo exporta el comando haciéndolo disponible para ser enlazado al modelo de vista. De esta manera es diferido, pues el comando no se puede usar hasta que el módulo que lo contiene es cargado.

     

     

    Comandos globales

    Hay otros comandos que son de naturaleza general, por lo que es mejor crear una sola implementación global en vez de duplicarlo por todas partes. Los comandos globales pueden ser exportados y luego importados donde sean necesarios, de manera que una copia única es usada, en vez de múltiples instancias locales. Usando de nuevo el ejemplo “home”, los modelos de vista pueden ofrecer ese servicio a la vista sin conocer el módulo o ensamblaje que contiene su implementación dejando que MEF importe el comando cuando ya está disponible.

    Para más detalles pueden consultar el artículo Sharing Commands with MEF.

     

    Cadena de responsabilidades

    El patrón cadena de responsabilidades es una poderosa herramienta para administrar mensajes comunes que son procesados de manera diferente dependiendo de sus características. En este caso, usé MEF para proveer un mecanismo de administración que define un contrato o interfase estándar que consume ciertos tipos específicos de mensajes. Cada administrador exporta el contrato junto con metainformación describiendo el tipo de mensajes con los que es compatible. Un enrutador central se encarga de importar los administradores y escuchar los mensajes despachándolos a los administradores apropiados hasta que alguno responda diciendo que lo pudo procesar correctamente. En combinación con los comandos globales, este mecanismo provee un alto grado de extensibilidad a la aplicación.

    Por ejemplo, imaginemos un comando “imprimir” que está disponible a toda la aplicación pero está enlazado al elemento que va a ser impreso. Hay varios controladores que han exportado el contrato de impresión y que saben cómo manejar distintos tipos de datos. Cuando el controlador que maneja el objeto de destino es invocado, formatea la información para poder ser impresa y envía el resultado a la impresora. Para manejar un nuevo tipo de datos, basta con crear un nuevo controlador y exportar el contrato o interfase de impresión.

     

    Fábricas de clases con resolución de dependencias

    Gran cantidad de clases tienen dependencias en varios niveles. Un caso puede ser una vista compuesta donde se presenta una lista de artículos, y cada artículo es expuesto mediante un modelo de vista asociado que tiene dependencias en el agrupador de eventos, el bus de servicios, y otros contratos que son importados usando MEF. Tal escenario es simplificado gracias a ExportFactory. Con esa herramienta se puede crear un modelo de vista con todo y resolución de dependencias, mientras iteramos una lista de datos. Los datos son incorporados al modelo de vista y éste es enlazado con la vista correspondiente; en todo esto, MEF se encarga de resolver las dependencias.

     

    Vistas compatibles con la fase de diseño

    Si se trabaja en paralelo con un equipo de diseñadores, es sumamente importante tener vistas que se comporten adecuadamente en la fase o tiempo de diseño. Es fácil crear vistas que pueden ser asociadas con modelos de vista diferentes durante la etapa de diseño usando los atributos d:Design y d:DesignInstance, mientras que MEF asocia el modelo de vista real durante la ejecución. Para este fin se puede usar la interfase IPartImportsSatisfied, evitando que en las vistas y modelos de vista se invoque código destinado al tiempo de ejecución.

    Para más detalles pueden ver el artículo (en inglés) Usando MEF para crear modelos de vista compatibles con la fase de diseño.

     

    Motor de validación

    Los motores de validación por lo general exponen formas de verificar si se han violado reglas específicas. Con MEF fue fácil crear un mecanismo de validación flexible y extensible, exportando un contrato de uso para cada regla de validación. El motor entonces importa todas las reglas y provee acceso usando una interfase fluida. Pueden leer el artículo (en inglés) Validación fluida con MEF y PRISM para más detalles.

     

    Pruebas unitarias

    MEF fue de importancia crítica en nuestro éxito con las pruebas unitarias, principalmente por exponer como contratos todas las dependencias importadas. Durante las pruebas lo que hicimos fue no invocar a MEF, dejando que el sistema fuera inicializado usando componentes simulados o parciales.  Más información en el artículo (en inglés) Pruebas unitarias avanzadas en Silverlight usando Moq.

     

    Conclusión

    Obviamente muchos de estos ejemplos podrían ser implementados usando otras herramientas aparte de la Infraestructura de extensibilidad administrada. Una ventaja de MEF es que viene incluida en la plataforma de ejecución: no es una biblioteca provista por terceros, sino que es parte fundamental de .NET y de Silverlight 4.0. Basándome en mi experiencia al diseñar varias aplicaciones de gran escala en Silverlight, diría que MEF no sólo facilitó el hacerlas modulares, sino que también, al incluir tantas utilidades, redujo el tiempo de desarrollo. En especial con la habilidad de etiquetar los componentes exportados con metainformación y poder importar múltiples componentes usando el mismo contrato. Espero que esto les ayude con algunas ideas sobre cómo usar MEF en sus propias aplicaciones, y que también sirva para demostrar de que MEF está siendo usando con gran éxito en la producción de aplicaciones empresariales.

     

    Jeremy Likness

     

    Categories: MVVM Tags: , ,

    DataGrid, ColumnSeries y un toque de MVVM

    March 17th, 2011 12 comments

    image

    Recibí una pregunta pregunta en un post que había escrito previamente. La pregunta era como se puede cambiar una gráfica a la par con los datos de un DataGrid. La respuesta es ¡Con DataBinding!

    Muchas gracias.

    Ok ok, la explicación larga. Primero vamos a hacer un modelo para datos de prueba:

     

    Este modelo cuenta con dos propiedades (Name y Value) e implementa INotifyPropertyChanged para poder manejar el Binding de forma correcta.

    Ahora creamos el ViewModel. En una aplicación real deberías obtener dichos valores desde un webservice en tu servidor, pero para efectos prácticos, aquí inicializo una lista de objetos SampleData (nuestro modelo) en el constructor del ViewModel.

     

    Finalmente creamos nuestra vista en XAML:

     

    En la línea 13 creamos una instancia de nuestro ViewModel y la agregamos a nuestros recursos locales. Después, en la línea 17 asignamos dicho recurso al DataContext de nuestro LayoutRoot (en este caso el Grid que contiene todos los elementos de la vista). En la línea 30 definimos la propiedad Samples de nuestro ViewModel como la fuente de datos del DataGrid. En la línea 40 definimos el binding a la propiedad Value con un slider para modificar los datos fácilmente. Las líneas 55 y 57 se encargan de hacer lo mismo para la gráfica de barras.

    Nótese como los bindings se definien con el Mode=TwoWay para actualizar el ViewModel “de ida y vuelta”, es decir, que los cambios en la vista se reflejan en el ViewModel y viceversa.

    Puedes ver el ejemplo corriendo aquí. Y puedes descargar el código aquí.

    UPDATE: De pura casualidad encontré mi compañera Araceli Medina encontró un bug en el Silverlight Toolkit mientras jugaba con el demo. Estaba jugando con mover sliders y cambiar el tamaño de la ventana cuando noté que esto pasaba:

    image_thumb[2]

    El borde inferior de los rectangulos no se quedaba en la parte de abajo de la gráfica. No estoy seguro a qué se deba esto pero si se vuelve a cambiar el tamaño de la ventana el problema se corrije. El bug fue dado de alta en codeplex.

    [Artículo originalmente publicado en JAMolina.com]