Windows 8:Cómo crear un app lector de blogs (RSS)-Parte 5

Pasadas las primeras 4 partes del tutorial podemos decir que tenemos una buena parte de nuestro 'backend' terminado, estamos casi listos para comenzar a pensar en la User Interface (UI), pero antes vamos a crear una capa intermedia.

Crearemos un objeto encargado de orquestar las funcionalidades que ya hemos creado para 'servirlas' a la interfaz gráfica, en este artículo nos ocuparemos de una primera parte de esta tarea que es ofrecer funcionalidades para consumir el modelo de datos y llenarlo de información, posteriormente retomaremos este trabajo para complementarlo con otras funcionalidades requeridas por la UI.

Inicializando la Aplicación e implementado el View-Model

EL ViewModel es una clase que a ojos de la UI será el modelo de datos, pero a ojos del modelo de datos será el consumidor de los datos (algo así como una UI), esta clase se crea para ordenar mejor nuestro código y delegar a cada componente solo una responsabilidad a la vez.

Sin embargo no nos lo tomaremos tan a pecho y puede que hagamos alguna trampita menor por el camino.

En Visual Studio damos clic sobre la carpeta ViewModel y agregamos una nueva clase la cual llamaremos RssMainViewModel esta clase debe ser publica, no estática y no sorpresivamente heredar de BindableBase

 using RSSJuanK4Blog.Common;
using RSSJuanK4Blog.Model;

namespace RSSJuanK4Blog.ViewModel
{
    public class RssMainViewModel : BindableBase
    {
    }
}

Ahora dentro del ViewModel creamos nuestra fuente de datos, a saber una colección de tipo ArticleList, dado que la expondremos como modelo es muy conveniente implementar el sistema de notificación de cambios a la UI , esto en resumen es llamar al método SetProperty del BindableBase como lo hicimos en las entradas anteriores.

 public class RssMainViewModel : BindableBase
{
    private ArticleList _articles = new ArticleList();
    public ArticleList Articles
    {
        get { return _articles; }
        set
        {
            SetProperty(ref _articles, value);
        }
    }

    private string _feedUrlString;
    public string FeedUrlString
    {
        get { return _feedUrlString; }
        set
        {
            SetProperty(ref _feedUrlString, value);
        }
    }
}

Como ven he dejado ya inicializado la instancia privada del objeto lo cual nos resultara muy conveniente cuando hagamos Binding desde la UI más adelante (Siguiente artículo).

Por el momento la colección esta vacía, hay que llenarla con los datos de una fuente RSS para lo cual usamos la clase RSSHelper creada en el articulo anterior.

Para hacer nuestro código más limpio la Url del feed será expuesta en el ViewModel como una propiedad pública.

Pero hay que tener especial atención con algo, cuando vinculemos nuestro ViewModel con la UI hay que tener presente que los datos deben aparecer en la UI y el proceso de carga de dicha información desde el feed no debe impactar la experiencia de usuario, es decir no debe bloquearlo ni darle la sensación de lentitud. Por ende el proceso de inicialización debe ser controlado a través de un método async que se llame posteriormente y no a través del constructor de la clase ya que este nunca se ejecuta en modo async y por ende bloquearía la UI hasta cargar.

Así que creamos un método asíncrono llamado Initialize, este se encargaría de esta importante labor, internamente llamamos al RSSHelper y cargamos los datos del feed en la lista. Para ello adicionamos los siguientes namespace:

 using RSSJuanK4Blog.Util;
using System.Threading.Tasks;

Y este es el código, muy sencillo por cierto gracias a nuestro trabajo en la parte 4:

 public async Task Initialize()
{
    Articles = await RSSHelper.GetArticleListFromFeedAsync(this.FeedUrlString);
}

Cómo y desde donde llamar a Initialize

Para llamar Initialize primero debemos crear una instancia de RssMainViewModel, normalmente ya supondrías que hay que crearlo en el View, esto es cierto, pero tengamos cuidado, es nuestro deber hacer tanto como sea posible para evitar que el View asuma responsabilidades que son del ViewModel. Por fortuna desde XAML se pueden crear instancias de clases.

Desde Visual Studio abrimos el archivo View/RssMainView.xaml y dentro el primer tag adicionamos como namespace el namespace donde esta RssViewModel

 xmlns:vm="using:RSSJuanK4Blog.ViewModel"

Quedando máso menos así (código recortado para dejar solo lo importante)

 <common:LayoutAwarePage
...
...
    xmlns:vm="using:RSSJuanK4Blog.ViewModel"
    >

Unas líneas más abajo dentro del bloque con el tag Page.Resources agregamos el siguiente código, el cual básicamente hace tres cosas

  • Crea una instancia de RssMainViewModel
  • Le asigna un name y un key
  • Inicializa la propiedad que guarda la url del feed
 
<vm:RssMainViewModel x:Key="ViewModel" x:Name="ViewModel" 
                     FeedUrlString="https://blogs.msdn.com/b/juank/rss.aspx"/>

Con esto ya tenemos una referencia a RssMainViewModel, ahora si a invocar el método Initialize.

Como ya mencione hay varias formas de hacerlo, lo más fácil es crear un manejador para el evento Loaded dentro de la propia View, pero eso no sería muy limpio.

Crearemos el manejador del evento Loaded dentro de RssMainViewModel.cs , y allí únicamente preguntamos si ya se ha asignado la url del feed para proceder a cargarlo

 public async void ViewLoadHandler(object o, RoutedEventArgs e)
{
    if (!string.IsNullOrWhiteSpace(FeedUrlString))
        await Initialize();
}

La idea es que cuando el View ya este totalmente inicializado proceda a llamar este evento, por ello el evento Loaded del View debemos asignarlo al manejador creado anteriormente, en condiciones ideales esto debería hacerse desde XAML pero la versión actual de WinRT aún no lo permite.

Vamos a RssMainView.xaml.cs y en el constructor asignamos al evento Loaded a el manejador que hemos creado en el ViewModel:

 public RssMainView()
{
    this.InitializeComponent();
    //Este es el código nuevo
    this.Loaded += ViewModel.ViewLoadHandler;
}

Así las cosas cuando el View cargue por completo este invocara el evento Loaded, que al ser evento es asíncrono por defecto, y este ejecutara el manejador que le acabamos de asignar, el cual es que inicializa la colección de Artículos.

Asociando el DataContext del View

Ya tenemos nuestra fuente de datos (el ViewModel) creada e inicializada desde el View, pero eso lo podemos hacer con cualquier objeto que necesitemos. Es necesario indicarle al View que ese objeto que hemos creado será nuestro contexto de datos, es decir el objeto principal desde donde obtendremos información.

Nuevamente en condiciones ideales esto debería hacerse desde XAML, es posible hacerlo pero para ello deberíamos haber creado el ViewModel como un objeto global para toda la aplicación y no solo para este View, lo cual realmente no es algo conveniente en la mayoría de los casos.

Necesitamos un mecanismo para decirle al View que fuente de datos usar, nuevamente la opción fácil es hacerlo desde el evento Loaded el cual ya tenemos implementado de manera adecuada para inicializar la lista, así que lo que haremos será reutilizar ese método para de paso asignar el DataContext.

Lo bueno de manejarlo como manejador de evento es que en el primer parámetro del método siempre llega el componente que ha disparado el evento, en este caso es nuestro View, así que debemos asignarle el DataContext pero como ha llegado como un object este no exhibe DataContext alguno, así que preguntamos si ese objeto es un FrameworkElement, la clase padre de los objetos de UI en XAML, y si lo es entonces procedemos a asignar el DataContex...

Cuál DataContext? El DataContext es el propio ViewModel y dado que en el manejador de evento estamos dentro del ViewModel pues el DataContext lo hacemos igual a this, es decir al objeto actual :).

Así queda ahora el manejador del evento Load

 public async void ViewLoadHandler(object o, RoutedEventArgs e)
{
    var tmpView = o as FrameworkElement;

    if (tmpView != null)
        tmpView.DataContext = this;

    if (!string.IsNullOrWhiteSpace(FeedUrlString))
        await Initialize();
}

Para que el código anterior funcione adecuadamente es necesario adicionar el siguiente using

 using Windows.UI.Xaml;

GENIAL! compilamos y ya tenemos nuestro ViewModel cargando toda la info del RSS y conectado adecuadamente con el View!

Este es el código completo de RssMainViewModel.cs

 using RSSJuanK4Blog.Common;
using RSSJuanK4Blog.Model;
using RSSJuanK4Blog.Util;
using System.Threading.Tasks;
using Windows.UI.Xaml;

namespace RSSJuanK4Blog.ViewModel
{
    public class RssMainViewModel : BindableBase
    {
        private ArticleList _articles = new ArticleList();
        public ArticleList Articles
        {
            get { return _articles; }
            set
            {
                SetProperty(ref _articles, value);
            }
        }

        private string _feedUrlString;
        public string FeedUrlString
        {
            get { return _feedUrlString; }
            set
            {
                SetProperty(ref _feedUrlString, value);
            }
        }

        public async Task Initialize()
        {
            Articles = await RSSHelper.GetArticleListFromFeedAsync(this.FeedUrlString);
        }

        public async void ViewLoadHandler(object o, RoutedEventArgs e)
        {
            var tmpView = o as FrameworkElement;

            if (tmpView != null)
                tmpView.DataContext = this;

            if (!string.IsNullOrWhiteSpace(FeedUrlString))
                await Initialize();
        }
    }
}

En el próximo artículo crearemos los componentes de la UI que muestran la información tomándola del ViewModel.

Índice General

Para cumplir con el alcance establecido he decidido fraccionar el proyecto en las siguientes partes:

 

  1. Windows 8:Cómo crear un app lector de blogs (RSS)-Parte 1
    • Introducción al tutorial  
    • Let's begin
    • Indice General
  2. Windows 8:Cómo crear un app lector de blogs (RSS)-Parte 2
    • Preparando la solución
  3. Windows 8:Cómo crear un app lector de blogs (RSS)-Parte 3
    • Modelo de Datos
  4. Windows 8:Cómo crear un app lector de blogs (RSS)-Parte 4
    • Consumiendo el RSS por medio de SyndicationClient
    • CreateContent
    • CreateSummary
    • Find1stImageFromHtml
  5. Windows 8:Cómo crear un app lector de blogs (RSS)-Parte 5
    • Inicializando la Aplicación e implementado el View-Model
    • Cómo y desde donde llamar a Initialize
    • Asociando el DataContext del View
  6. Windows 8:Cómo crear un app lector de blogs (RSS)-Parte 6
    • Construyendo la UI - Parte 1
    • Esquema principal de la App
    • Creando elementos básicos
    • El titulo
    • Aplicar propiedades utilizando estilos
    • El icono
    • El artículo actual
    • La Lista de Artículos
  7. Windows 8:Cómo crear un app lector de blogs (RSS)-Parte 7
    • Vinculando la View con el ViewModel
    • El artículo actual
  8. Windows 8:Cómo crear un app lector de blogs (RSS)-Parte 8
    • Mejorando la experiencia de usuario - Parte 1
    • Hacer que aparezca un articulo seleccionado por defecto
    • Disminuir el tamaño de los títulos del ListView
    • Disminuir el ancho del ListView
    • Evitar que los resúmenes de los artículos en el ListView crezcan de manera descontrolada
    • Colocar una imagen dummy en el Listview cuando no existan imágenes en el artículo
    • Colocar la imagen adecuada cuando la única imagen del RSS es el aggbug
    • Colocar una imagen dummy en el Listview cuando la imagen hallada en el artículo sea demasiado pequeña
    • Mientras cargan los datos del feed da la impresión de que la App no esta haciendo nada
    • Conclusión
  9. Windows 8:Cómo crear un app lector de blogs (RSS)-Parte 9
    • Mejorar la apariencia de ListView
    • Mejorar la apariencia del ProgressRing
    • Mejorar la apariencia del WebView
    • Soporte para Snapped View
    • Imagen de Fondo
  10. Windows 8:Cómo crear un app lector de blogs (RSS)-Parte 10
    • Mejorando la experiencia de usuario - Parte 2
    • Detección de conexión a internet
    • Adición de la política de privacidad
    • Tareas adicionales
    • FIN DEL TUTORIAL