Windows 8:Cómo crear un app lector de blogs (RSS)-Parte 10
Mejorando la experiencia de usuario - Parte 2
Este es el último post de esta serie, solo nos falta afinar estos dos importantes temas:
- Detección de conexión a internet
- Adición de la política de privacidad
Detección de conexión a internet
Cuando estamos llamando a la fuente RSS para armar los contenidos debemos controlar si hay o no conexión a internet para evitar que la aplicación se reviente.
En un artículo de este mismo blog enseñe a hacerlo:
Cómo detectar si hay conexión a Internet en WinRT C#
Ahora integraremos es funcionalidad con nuestra App.
Desde Visual Studio abrimos Util/RSSHelper.cs y adicionamos el código correspondiente al artículo mostrado anteriormente.
En el método GetArticleListFromFeedAsync hacemos las modificaciones necesarias para validar la conexión a internet justo antes de traer el feed
public static async Task<ArticleList> GetArticleListFromFeedAsync(string feedUrl)
{
var syncClient = new SyndicationClient();
var lista = new ArticleList();
if (InternetConectivity)
{
var feed = await syncClient.RetrieveFeedAsync(new Uri(feedUrl));
foreach (var art in feed.Items)
{
var content = CreateContent(art.NodeValue);
lista.Add(new Article()
{
Title = art.Title.Text,
Content = content,
Summary = CreateSummary(art.Summary, content),
ImgUri = Find1stImageFromHtml(content)
});
}
}
return lista;
}
Y sino hay conexión...
Si no hay conexión es conveniente lanzar una excepción para que desde el View se atrape este error y se le de el tratamiento adecuado.
Definimos una variable con un texto relacionado a la excepción y cuando no haya conexión lanzamos la excepción correspondiente:
private const string INTERNET_REQUIRED = "Esta aplicación requiere acceso a internet para funcionar adecuadamente";
public static async Task<ArticleList> GetArticleListFromFeedAsync(string feedUrl)
{
var syncClient = new SyndicationClient();
var lista = new ArticleList();
if (InternetConectivity)
{
var feed = await syncClient.RetrieveFeedAsync(new Uri(feedUrl));
foreach (var art in feed.Items)
{
var content = CreateContent(art.NodeValue);
lista.Add(new Article()
{
Title = art.Title.Text,
Content = content,
Summary = CreateSummary(art.Summary, content),
ImgUri = Find1stImageFromHtml(content)
});
}
}
else
{
throw new Exception(INTERNET_REQUIRED);
}
return lista;
}
Necesitamos que cuando no haya conexión a internet se despliegue un mensaje informando al usuario de la situación. Desde Visual Studio, en la carpeta Util, creamos una nueva clase llamada MessageHelper que luce como se ve a continuación
using System;
using System.Threading.Tasks;
using Windows.UI.Popups;
namespace RSSJuanK4Blog.Util
{
public static class MessageHelper
{
public static async Task ShowMessageAsync(string content, string title)
{
MessageDialog msg;
if (!string.IsNullOrWhiteSpace(title))
msg = new MessageDialog(content, title);
else
msg = new MessageDialog(content);
msg.Options = MessageDialogOptions.AcceptUserInputAfterDelay;
await msg.ShowAsync();
}
}
}
Ahora en ViewModel/RSSMainViewModel.cs método Initialize capturamos la posible excepción generada y recuperamos el mensaje de la misma.
Ya con el mensaje, lo evaluamos y si hay error entonces mostramos el MessageDialog y después de ello cerramos la aplicación:
public async Task Initialize()
{
string exMessage = "";
try
{
Articles = await RSSHelper.GetArticleListFromFeedAsync(this.FeedUrlString);
}
catch (Exception e)
{
exMessage = e.Message;
}
if (!string.IsNullOrWhiteSpace(exMessage))
{
await MessageHelper.ShowMessageAsync(exMessage, "Houston, tenemos un problema!");
App.Current.Exit();
}
FirstOrDefaultArticle = Articles.FirstOrDefault();
IsLoading = false;
}
Mientras el mensaje se muestra debemos asegurarnos que la Lista no se muestre ni tampoco el ProgressRing para brindar una apariencia más agradable. El ProgressRing lo podemos cambiar haciendo uso de la propiedad IsLoading como lo vimos en anteriores artículos,para la Lista creamos una Propiedad ShowList de manera similar y ambas las asignamos cuando corresponda quedando el código así
private bool _showList;
public bool ShowList
{
get { return _showList; }
set { SetProperty(ref _showList, value); }
}
if (!string.IsNullOrWhiteSpace(exMessage))
{
IsLoading = false;
ShowList = false;
await MessageHelper.ShowMessageAsync(exMessage, "Houston, tenemos un problema!");
App.Current.Exit();
}
else {
ShowList = true;
}
La parte lógica ya esta lista, ahora hay que integrarla la con el View, desde Visual Studio editamos Appx.xaml y adicionamos dos recursos nuevos al inicio, los cuales nos permiten convertir de booleano a la enumeración Visibility.
<common:BooleanToVisibilityConverter x:Key="BoolToVisConv" />
<common:BooleanNegationConverter x:Key="NegBoolToVisConv" />
Desde luego para que lo anterior funcione hay que adicionar el namespace common en la parte superior del tag principal
<Application
x:Class="RSSJuanK4Blog.App"
...
xmlns:common="using:RSSJuanK4Blog.Common"
>
Ahora editamos View/RssMainPage.xaml, en el ListView lvwBlogPosts hacemos que el atributo Visibility haga Binding con la propiedad ShowList de la siguiente forma:
<ListView x:Name="lvwBlogPosts" Grid.ColumnSpan="2" Grid.Row="1"
Style="{StaticResource Lista-Posts-Style}"
ItemsSource="{Binding Articles}"
ItemTemplate="{StaticResource Post-List-ItemTemplate}"
SelectedItem="{Binding FirstOrDefaultArticle}"
ItemContainerStyle="{StaticResource Post-List-ItemContainerTemplate}"
Visibility="{Binding ShowList,Converter={StaticResource BoolToVisConv}}"
/>
Adición de la política de privacidad
Las aplicaciones que consumen o envían datos a internet deben incluir una política de privacidad.
Esta política debe informar al usuario acerca del uso que se le da a la información enviada por medio de la aplicación lo cual pueden ser desde URLs a las que accede hasta la recopilación de datos personales, dicha política debe ser fácilmente accesible/descubrible desde la propia aplicación.
Adicionalmente si dicha información se comparte con terceros, para cualquier fin, la aplicación debe contar con un mecanismo que autorice o deniegue dicho uso y este debe ser igualmente fácilmente accesible/descubrible.
Para el caso de la aplicación que se esta creando en este tutorial se debe incluir la política puesto que enviamos y recibimos datos por internet, que datos enviamos? actualmente solo los request hechos a la web pero dado que estos request son medibles y me permitirían utilizarlos para establecer por ejemplo las preferencias de un usuario.
La forma recomendada para mostrar la política es haciendo uso del Settings Charms, es un proceso sencillo, haremos uso de lo explicado en este artículo:
Como crear y/o utilizar el Charm de Settings en WinRT - C# - XAML
Así que básicamente debemos crear un control con la información de la política y atarlo al Charm, utilizaremos la funcionalidad de SettingsHelper del artículo citado.
En ViewModel/RssMainViewModel.cs vamos al método Initialize, es momento de hacer algo de refactoring, el código que esta actualmente en ese método tiene por objeto inicializar los componentes gráficos con la información del modelo, extraeremos esto en un método llamado UIInitializer y crearemos un nuevo método llamado SettingsInitializer .
public async Task Initialize()
{
await UIInitializer();
}
private async Task UIInitializer()
{
string exMessage = "";
try
{
Articles = await RSSHelper.GetArticleListFromFeedAsync(this.FeedUrlString);
}
catch (Exception e)
{
exMessage = e.Message;
}
if (!string.IsNullOrWhiteSpace(exMessage))
{
ShowList = false;
IsLoading = false;
await MessageHelper.ShowMessageAsync(exMessage, "Houston, tenemos un problema!");
App.Current.Exit();
}
else
{
ShowList = true;
}
FirstOrDefaultArticle = Articles.FirstOrDefault();
IsLoading = false;
}
Después de llamar a UIInitializer asignamos el manejador de evento y en el creamos la opción de menú.
public async Task Initialize()
{
await UIInitializer();
SettingsPane.GetForCurrentView().CommandsRequested += MainPage_CommandsRequested;
}
private void MainPage_CommandsRequested(SettingsPane sender, SettingsPaneCommandsRequestedEventArgs args)
{
args.Request.ApplicationCommands.Clear();
var jkCommand = new SettingsCommand("ppolicy", "Política de Privacidad",
(handler) =>
{
var settingsHelper = new SettingsWindowHelper();
settingsHelper.ShowFlyout(new PrivacyPolicyUC());
});
args.Request.ApplicationCommands.Add(jkCommand);
}
De lo anterior debemos crear el UserControl llamado PrivacyPolicyUC.
Desde Visual Studio creamos una nueva carpeta llamada "User Controls", a su vez dentro de ella creamos un nuevo UserControl llamado PrivacyPolicyUC.
En el colocamos un botón un título y un texto de la siguiente forma.
<UserControl
x:Class="RSSJuanK4Blog.User_Controls.PrivacyPolicyUC"
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:RSSJuanK4Blog.User_Controls"
xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<Grid Background="#001E4E"
Style="{StaticResource LayoutRootStyle}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Button Width="Auto" Height="Auto" x:Name="CloseButton"
Margin="10,10,0,0" Style="{StaticResource BackButtonStyle}"
Click="CloseButton_Click"
/>
<RichTextBlock Grid.Column="1" Grid.Row="0" Margin="0,20,20,0"
Grid.RowSpan="2">
<Paragraph FontSize="25" TextAlignment="Center" >
<Run>PolÃtica de Privacidad</Run>
<LineBreak/>
<LineBreak/>
</Paragraph>
<Paragraph>
Esta aplicación no recopila ni hace uso de la información del usuario, solo utiliza la conexión a internet para descargar contenidos del feed.
</Paragraph>
</RichTextBlock>
</Grid>
</UserControl>
Agregamos este método en el code behind
private void CloseButton_Click(object sender, RoutedEventArgs e)
{
//Referenciar el Popup que es el control padre de este user control
var pop = this.Parent as Popup;
//Si el padre es en efecto un Popup cerrarlo
if (pop != null)
pop.IsOpen = false;
//Mostrar el SettingsPane
SettingsPane.Show();
}
Al ejecutarlo esta perfectamente funcional, solo que el WebView siempre parece estar por encima del UserControl:
Para solucionar esto seguimos las recomendaciones dadas en este artículo:
El extraño caso del WebView que siempre mantiene visible sobre los demás controles – WinRT – C#
Por lo cual en RssMainView.xaml creamos un control de tipo Rectangle y su correspondiente estilo en Appx.xaml
<Rectangle x:Name="wvWrapper" Grid.Column="2" Grid.Row="1" Style="{StaticResource wvWrapper-Style}"/>
<Style x:Key="wvWrapper-Style" TargetType="Rectangle">
<Setter Property="Margin" Value="10,0"/>
<Setter Property="CacheMode" Value="BitmapCache"/>
</Style>
Aprovechando lo enseñado en el artículo del WebView y la funcionalidad del artículo de SettingsWindowHelper modificamos en RssMainViewModel.cs el método que inicializa los settings, para que se encargue de alternar entre el WebView verdadero y la copia en imagen según se requiera:
private void MainPage_CommandsRequested(SettingsPane sender, SettingsPaneCommandsRequestedEventArgs args)
{
args.Request.ApplicationCommands.Clear();
var jkCommand = new SettingsCommand("ppolicy", "PolÃtica de Privacidad",
(handler) =>
{
var settingsHelper = new SettingsWindowHelper();
var brush = new WebViewBrush();
brush.SetSource(WebViewControl);
brush.Redraw();
wvWrapper.Fill = brush;
WebViewControl.Visibility = Visibility.Collapsed;
settingsHelper.ShowFlyout(new PrivacyPolicyUC(),
() => WebViewControl.Visibility = Visibility.Visible
);
});
args.Request.ApplicationCommands.Add(jkCommand);
}
Tareas adicionales
La app esta lista! en el archivo Package.appxmanifest de la solución podemos (aunque yo diría que DEBEMOS) configurar todas las diferentes imágenes necesarias para nuestra App y adicionar detalles como descripción etc.
Para que la publicación de la aplicación sea exitosa conviene revisar este artículo al respecto:
Check List para publicación de Apps en Windows Store
FIN DEL TUTORIAL
Eso es todo!
La solución completa pueden utilizarla como referencia o como prefieran y esta disponible aquí via GitHub:
Índice General
Para cumplir con el alcance establecido he decidido fraccionar el proyecto en las siguientes partes:
- Windows 8:Cómo crear un app lector de blogs (RSS)-Parte 1
- Introducción al tutorial
- Let's begin
- Indice General
- Windows 8:Cómo crear un app lector de blogs (RSS)-Parte 2
- Preparando la solución
- Windows 8:Cómo crear un app lector de blogs (RSS)-Parte 3
- Modelo de Datos
- Windows 8:Cómo crear un app lector de blogs (RSS)-Parte 4
- Consumiendo el RSS por medio de SyndicationClient
- CreateContent
- CreateSummary
- Find1stImageFromHtml
- 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
- 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
- Windows 8:Cómo crear un app lector de blogs (RSS)-Parte 7
- Vinculando la View con el ViewModel
- El artículo actual
- 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
- 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
- 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
Comments
Anonymous
November 26, 2012
Introducción al tutorial Hola, antes de comenzar con lo obvio quiero que se fijen en el titulo de esteAnonymous
November 26, 2012
Preparando la solución Para este proyecto vamos a Visual Studio y creamos una nueva solución de Tipo Windows Store, seleccionamos la plantilla en blanco y la nombramos RSSJuanK4BlogAnonymous
November 26, 2012
The comment has been removedAnonymous
November 26, 2012
The comment has been removedAnonymous
November 26, 2012
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 datosAnonymous
November 26, 2012
The comment has been removedAnonymous
November 26, 2012
The comment has been removedAnonymous
November 26, 2012
Mejorando la experiencia de usuario - Parte 1 Aplicación terminada? no. Funcionalidad terminada? mmm digamos que si. La aplicación ya hace lo mínimo funcional pero hay que hacerle varios ajustes y mejoras para brindar al usuario una experiencia más agradableAnonymous
November 26, 2012
Construyendo la UI - Parte 2 En este artículo trabajaremos sobre algunos aspectos de la Interfaz de UsuarioAnonymous
November 26, 2012
The comment has been removedAnonymous
November 26, 2012
The comment has been removedAnonymous
November 26, 2012
The comment has been removedAnonymous
December 10, 2012
Hola estuve siguiendo este tutorial, muy bueno por cierto, para utilizarlo en una aplicación parecida, lo que pasa es que yo quiero extraer la información de un vídeo de youtube, no si se me podrías orientar con el tema de las cabeceras ya que estoy utilizando la clase HttpWebRequest y estuve buscando la propiedad UserAgent pero creo que ya no la utilizan... Te pediría que ne ayudes por favor, GraciasAnonymous
December 13, 2012
Wilson, explícame un poco más para ver como te puedo ayudar porque no me quedo muy claro lo que requieres.Anonymous
February 01, 2013
Saludo y disculpa ya lo tengo casi completo pero tengo tres errores que son: Error 1 The name "BooleanToVisibilityConverter" does not exist in the namespace "using:RSSJuanK4Blog.Common". C:UsersJonathandocumentsvisual studio 2012ProjectsLector de blogLector de blogApp.xaml 1 1 Lector de blog Error 2 The name "BooleanNegationConverter" does not exist in the namespace "using:RSSJuanK4Blog.Common". C:UsersJonathandocumentsvisual studio 2012ProjectsLector de blogLector de blogApp.xaml 1 1 Lector de blog Error 3 'Lector_de_blog.Templates.Templates' does not contain a definition for 'InitializeComponent' and no extension method 'InitializeComponent' accepting a first argument of type 'Lector_de_blog.Templates.Templates' could be found (are you missing a using directive or an assembly reference?) C:UsersJonathandocumentsvisual studio 2012ProjectsLector de blogLector de blogTemplatesTemplates.xaml.cs 26 18 Lector de blogAnonymous
February 01, 2013
RAMON, omitiste un paso de los capitulkos anteriores, rapidamente, agrega a tu proyecto un nuevo item de tipo BAsic Page (no blank page) te hara luego una pregunta y dile que si.Anonymous
March 24, 2013
Introducción al tutorial Hola, antes de comenzar con lo obvio quiero que se fijen en el titulo de esteAnonymous
March 24, 2013
Introducción al tutorial Hola, antes de comenzar con lo obvio quiero que se fijen en el titulo de esteAnonymous
March 24, 2013
Introducción al tutorial Hola, antes de comenzar con lo obvio quiero que se fijen en el titulo de esteAnonymous
March 24, 2013
Introducción al tutorial Hola, antes de comenzar con lo obvio quiero que se fijen en el titulo de esteAnonymous
March 24, 2013
Preparando la solución Para este proyecto vamos a Visual Studio y creamos una nueva solución de Tipo Windows Store, seleccionamos la plantilla en blanco y la nombramos RSSJuanK4BlogAnonymous
March 24, 2013
The comment has been removed