[Windows Phone 7.5] Desarrollo nativo vs PhoneGap.

Hola a todos!

DISCLAIMER:

Este artículo solo representa mi opinión.

Dicho esto, si sigues leyendo es porque te interesa saber porqué llego a esta conclusión. Hasta ahora me había alejado todo lo posible de javascript y html, los lenguajes del futuro que salvarán al mundo de la crisis y harán que el sol luzca más y la lluvia moje menos. Pero esta semana he tenido que trabajar en un proyecto en PhoneGap, la verdad es que me apetecía probar como funcionaba y como era la experiencia tanto del lado del desarrollador como del lado del usuario. Y tras unos días realmente sorprendentes, aquí van mis conclusiones y algo de código y ejemplos para probarlas.

PhoneGap, javascript, html…

La verdad es que todo aquel que me conozca sabrá que no me caracterizo por mi amor a javascript o html, pero debo reconocer que el trabajar con ellos esta semana ha sido menos doloroso de lo que me esperaba. En gran parte gracias a la ayuda de mi compañero Gerard López, un auténtico crack de PhoneGap, javascript y demás hierbas. En parte por el stack que el mismo ha escogido, formado por knockout.js, jquery mobile y PhoneGap, que realmente facilita mucho la vida del desarrollador. Lo reconozco, me lo he pasado muy bien con javascript… es algo que no me esperaba jejejeje

Pero aunque el lado del desarrollador me ha sorprendido gratamente, me ha decepcionado mucho el lado de usuario. Me explico, por lo que he visto, el rendimiento de las aplicaciones, al menos en Windows Phone, no es ni de lejos el mismo y después de verlo por mis propios ojos no creo que se pueda llegar a ofrecer una buena experiencia de usuario con PhoneGap en Windows Phone. Pero las palabras se las lleva el viento, vamos a ver varios ejemplos de ello.

Mapas y más mapas

Para empezar veamos un ejemplo de uso de mapas, el objetivo del ejercicio es tener un mapa en pantalla centrado en unas coordenadas específicas (el centro del mundo) y con un zoom de nivel 8.

En PhoneGap, añadimos una referencia al api javascript de google:

Api google
  1. <script type=»text/javascript» src=»https://maps.google.com/maps/api/js?sensor=true»></script>

En nuestro <BODY> incluimos un <DIV> llamado “map”:

Body
  1. <body onload=»load()»>
  2. <div id=»map» styleHeight:100%; width:100%;»></div>
  3. </body>

Y en el evento onload del <BODY> cargamos el mapa de google en nuestro <DIV>:

función load
  1. function load() {
  2.     var latlng = new google.maps.LatLng(43.409038, -3.159943);
  3.     var myOptions = { zoom: 8, center: latlng, mapTypeId: google.maps.MapTypeId.ROADMAP };
  4.     var map = new google.maps.Map(document.getElementById(«map»), myOptions);
  5. }

Nadie en su sano juicio puede pensar que este código es complicado, de hecho está copiado de la página de google sobre como usar el api de google maps.

¿El resultado?

image

Voilá! Con solo tres líneas de javascript tenemos un mapa de google totalmente funcional.

¿Y Silverlight? Bueno, empezaremos por añadir a nuestro proyecto una referencia al ensamblado Microsoft.Phone.Controls.Map y a continuación añadiremos una referencia en nuestra página:

Referencia al control map
  1. xmlns:map=»clr-namespace:Microsoft.Phone.Controls.Maps;assembly=Microsoft.Phone.Controls.Maps»

A continuación, solo tenemos que insertar el control en nuestra página y establecer sus propiedades ZoomLevel y Center:

Control map
  1. <Grid x:Name=»ContentPanel» Grid.Row=»1″ Margin=»12,0,12,0″>
  2.     <map:Map ZoomLevel=»8″ Center=»43.409038, -3.159943″></map:Map>
  3. </Grid>

Y listo, ejecutamos y obtenemos el siguiente resultado:

image

Así a primera vista, salvo que en PhoneGap usamos Google maps y en Silverlight usamos Bing maps, no podemos decir que haya existido una complejidad mucho mayor en uno u otro caso, y las diferencias visuales son anecdóticas… ¿Cual es el problema entonces? Mejor lo vemos en un vídeo:

Como podemos observar en el vídeo hay una gran diferencia de rendimiento en la aplicación de PhoneGap con respecto a la aplicación de Windows Phone escrita en Silverlight. También el arranque de la aplicación se ve más lento en PhoneGap, aunque esto es más debido a que se trata el primer inicio y está pasando nuestro código html y todas las dependencias al isolated storage de la aplicación para poder acceder a ellas.

Conclusión

Creo que el modo de desarrollo con javascript y html ha mejorado muchísimo, cosas como knockout.js o jquery hacen que sea mucho más sencillo trabajar, pero también creo que el rendimiento que ofrece javascript y html, al menos en Windows Phone, es horrible y justifica de sobra desarrollar en nativo. ¿Tendremos una sorpresa en cuanto a rendimiento en Windows Phone 8? Quizás, pero si no es así, no creo que puedas crear una aplicación de calidad para Windows Phone con PhoneGap. Aquí os dejo como siempre el proyecto de ejemplo, que contiene las dos aplicaciones con el mapa para que se vea que no hay trampa. Para poder usar PhoneGap os podéis descargar la última versión y encontrar instrucciones sobre como instalarlo aquí.

Un saludo y Happy Coding!

[Windows Phone 7.5 tip] Listas infinitas

Hola a todos!

Estos días he estado jugando con el listbox de Windows Phone para hacer una lista infinita, es decir, que cuando el usuario haga scroll hasta el final de la lista, está se lo notifique al ViewModel para que se pidan más datos. De esta forma podemos evitar el cargar demasiados datos al inicio, lo que ralentizaría la carga e ir solicitándolos a medida que el usuario los necesite, podemos ver un ejemplo del funcionamiento de esta técnica en la mayoría de las aplicaciones de twitter de Windows Phone.

Método “oficial” nuevos VisualSates

Internamente un listbox se compone, entre otros controles, de un ScrollViewer que le otorga la capacidad de movimiento por contenido que ocupe más que el área de visualización de la pantalla. Con Windows Phone 7.5 Microsoft añadió dos visual groups nuevos al ScrollViewer: VerticalCompression, compuesto por los visual states CompressionTop y CompressionBottom y HorizontalCompression, que a su vez se compone de CompressionLeft y CompressionRight:

ScrollViewer Visual States
  1. <VisualStateManager.VisualStateGroups>
  2.     <VisualStateGroup x:Name=»ScrollStates»>
  3.         <VisualStateGroup.Transitions>
  4.             <VisualTransition GeneratedDuration=»00:00:00.5″/>
  5.         </VisualStateGroup.Transitions>
  6.         <VisualState x:Name=»Scrolling»>
  7.         </VisualState>
  8.         <VisualState x:Name=»NotScrolling»>
  9.         </VisualState>
  10.     </VisualStateGroup>
  11.     <VisualStateGroup x:Name=»VerticalCompression»>
  12.         <VisualState x:Name=»NoVerticalCompression»/>
  13.         <VisualState x:Name=»CompressionTop»/>
  14.         <VisualState x:Name=»CompressionBottom»/>
  15.     </VisualStateGroup>
  16.     <VisualStateGroup x:Name=»HorizontalCompression»>
  17.         <VisualState x:Name=»NoHorizontalCompression»/>
  18.         <VisualState x:Name=»CompressionLeft»/>
  19.         <VisualState x:Name=»CompressionRight»/>
  20.     </VisualStateGroup>
  21. </VisualStateManager.VisualStateGroups>

De esta forma, podríamos obtener en primer lugar una referencia al ScrollViewer dentro de nuestro ListBox y a continuación subscribirnos al cambio de estado visual del mismo, para que cuando el estado fuese uno de los cuatro estados de compresión, actuásemos cargando datos. La verdad es que este método es un poco “farragoso” y nos exige meter bastante código extra en nuestra vista. Pero además tiene una exigencia especial:

En Windows Phone 7.5 el control ScrollViewer tiene una propiedad ManipulationMode que puede recibir dos valores distintos: Control o System. El valor Control indica que se manipula como cualquier otro control de Windows Phone, mientras que el valor System indica que el sistema se encargará de la manipulación del ScrollViewer. Para que los nuevos VisualStates funcionen, el ManipulationMode debe ser System, que es el valor por defecto.

En un principio parece una buena solución, pero cuando empecé a realizar pruebas, cargando elementos de 25 en 25, observé que tras varias cargas, cuando pinchabas sobre un elemento de la lista, se seleccionaba otro al azar. Este comportamiento se corregía simplemente con ir al principio de la lista y provocar un CompressionTop… y de hecho lo he sufrido muchas veces en algunas aplicaciones de twitter como Birdsong o Mehdoh… por lo que decidí no implementar este método y revisar mis feeds a ver quien más se había enfrentado a esto y como lo habían solucionado.

Si quieres ver el artículo oficial de MSDN sobre los nuevos visual states para el scrollviewer lo tienes aquí.

Método “Bueno”

Tras un poco de búsqueda por internet di con un artículo escrito por Daniel Vaughan (si no lo seguís en twitter, es buen momento para empezar a hacerlo) en CodeProject que explicaba un método alternativo de realizar una lista infinita.

En un ScrollViewer, siempre podemos obtener dos propiedades: ScrollableHeight y VerticalOffset . Básicamente, comparando estas dos propiedades podremos saber si hemos llegado al final de la lista, lo que explica Daniel en su artículo de CodeProject es además como crear un Helper que obtenga estos valores y lance la llamada a un comando que hayamos enlazado a este helper.

Básicamente solo tenemos que usar el Helper en nuestro XAML:

ScrollViewerMonitor
  1. <Grid x:Name=»ContentPanel» Grid.Row=»1″ Margin=»12,0,12,0″>
  2.     <ListBox ItemsSource=»{Binding Items}«
  3.                 FontSize=»{StaticResource PhoneFontSizeExtraLarge}«
  4.                 monitor:ScrollViewerMonitor.AtEndCommand=»{Binding GetMoreDataCommand}«>
  5.                 
  6.     </ListBox>
  7. </Grid>

Y listo, cada vez que lleguemos al final de la lista se pedirán nuevos elementos.

Conclusión

Bueno, hoy solo quería dejaros este tip, por si os veis en la misma situación alguna vez y sobre todo reconocer el buen trabajo de Daniel Vaughan, cuya solución me parece mucho mejor que la “oficial” implementada por Microsot. Aquí os dejo un pequeño ejemplo de como usar la implementación de la lista infinita usando el segundo método para que os sirva de referencia.

Un saludo y Happy Coding!

[Windows 8] Apps Metro para desarrolladores Windows Phone 7.5 (7 de N)

Hola a todos!

En esta séptima entrada de la serie vamos a revisar dos conceptos indispensables a la hora de crear la experiencia de usuario de una aplicación metro: la barra de aplicación y las orientaciones de la aplicación.

Barra de aplicación

En Windows Phone este componente está limitado por que al no ser un componente nativo de Silverlight, no podemos realizar enlace a datos para usar comandos, aunque existen alternativas que lo implementan, pero que nos exigen usar un componente de terceros para algo tan sencillo como la barra de aplicación.

En WinRT han solventado esto de una forma muy sencilla: la barra de aplicación es un contenedor, donde podremos incluir cualquier botón standard y aprovecharnos de sus capacidades naturales de ejecución de comandos o enlace a datos. Esto nos brinda un gran potencial, pero también implica un peligro: acabar creando “algo” que no es una barra de aplicaciones. Para evitar esto, Microsoft pone a nuestra disposición una guía de estilos muy concreta sobre la App bar.

Guía de Estilo de la barra de aplicación

Lo primero que nos recomienda esta guía es que hagamos un uso consistente de la barra de aplicaciones. Dado que esta se crea por página, no global a toda la aplicación, tenemos que ser especialmente cuidadosos con elementos que estén presentes en varias pantallas, posicionándolos siempre en la misma posición.

Otro punto a tener en cuenta es organizar nuestros comandos en grupos. Si tenemos dos grupos distintos de comandos, como un grupo de creación de contenido (nuevo, guardar, etc…) y uno de ordenación o filtrado, pondremos uno a la derecha y otro alineado a la izquierda. Si tenemos más de dos grupos, pondremos los más parecidos en un lado y los otros en el lado contrario:

image

Además es importante que hagamos esto de forma consistente en toda la aplicación, no podemos cambiar la alineación de los grupos entre pantallas, si decidimos que las opciones de filtrado vayan a la derecha, en todas las pantallas deben aparecer a la derecha.

Barra inferior y… superior

Mientras que en Windows Phone solo existía una barra de aplicación, situada siempre en la parte inferior de la pantalla, en Windows 8 tendremos dos barras de aplicación distintas: la barra inferior y la barra superior. El ejemplo anterior es de una barra inferior, donde ofreceremos opciones al usuario para interactuar con la pantalla que está viendo, sin embargo, en la barra superior ofreceremos opciones de navegación, como ir atrás, ir a otra pantalla o ir a diferentes secciones de la pantalla actual:

image

Para incluir cada una de estas barras, disponemos de dos propiedades dentro de nuestra página TopAppBar y BottomAppBar:

BottomAppBar y TopAppBar
  1. <Page
  2.     x:Class=»win8appbar.BlankPage»
  3.     xmlns=»http://schemas.microsoft.com/winfx/2006/xaml/presentation»
  4.     xmlns:x=»http://schemas.microsoft.com/winfx/2006/xaml»
  5.     xmlns:local=»using:win8appbar»
  6.     xmlns:d=»http://schemas.microsoft.com/expression/blend/2008″
  7.     xmlns:mc=»http://schemas.openxmlformats.org/markup-compatibility/2006″
  8.     mc:Ignorable=»d»>
  9.  
  10.     <Grid Background=»{StaticResource ApplicationPageBackgroundThemeBrush}«>
  11.     </Grid>
  12.     
  13.     <Page.BottomAppBar>
  14.         
  15.     </Page.BottomAppBar>
  16.     
  17.     <Page.TopAppBar>
  18.         
  19.     </Page.TopAppBar>
  20. </Page>

Una vez añadidos los tags a nuestro xaml, lo primero que tenemos que hacer es añadir la AppBar:

AppBar
  1. <Page.BottomAppBar>
  2.     <AppBar IsOpen=»True» Opacity=»1″ IsSticky=»True»>
  3.     </AppBar>
  4. </Page.BottomAppBar>
  5.  
  6. <Page.TopAppBar>
  7.     <AppBar IsOpen=»True» Opacity=»1″ IsSticky=»False»>
  8.     </AppBar>
  9. </Page.TopAppBar>

Este elemento tiene dos propiedades muy interesantes, IsOpen, que indica si la AppBar está desplegada o no e IsSticky, que nos indica si cuando el usuario interactúa con la interface la barra debe cerrarse. Por norma general, IsSticky debería ser false, pero puede que deseemos mostrar acciones de la barra de aplicación cuando el usuario ha seleccionado un elemento de la página, incluso cuando selecciona varios. Para estos casos debemos establecer la propiedad IsSticky a true para permitir que el usuario interactúe sin perder de vista la barra de aplicación.

Una vez hecho esto, añadir contenido es tan sencillo como cualquier otro contenedor de XAML:

Contenido AppBar
  1. <Page.BottomAppBar>
  2.     <AppBar IsOpen=»True» Opacity=»1″ IsSticky=»True»>
  3.         <StackPanel Orientation=»Horizontal»>
  4.             <Button BorderBrush=»Transparent» Height=»68″ Margin=»10,0,0,5″>
  5.                 <StackPanel Margin=»-10″>
  6.                     <Viewbox xmlns=»http://schemas.microsoft.com/winfx/2006/xaml/presentation»>
  7.                         <Grid Width=»48″ Height=»48″ Visibility=»Visible»>
  8.                             <Rectangle Fill=»#00000000″ Visibility=»Visible» />
  9.                             <Path Data=»M23.916957,4.0498749C24.661973,4.0498755,25.406989,4.6223834,25.971982,5.7673987L29.951907,13.837196C31.081895,16.117462,34.071845,18.29763,36.601836,18.657492L45.501613,19.957292C48.021598,20.317153,48.601668,22.077402,46.771617,23.857181L40.331763,30.137434C38.501712,31.917213,37.351828,35.427454,37.791762,37.947467L39.311759,46.817066C39.741687,49.327308,38.241706,50.417148,35.99174,49.227211L28.02188,45.037284C25.771912,43.857112,22.072013,43.857112,19.812035,45.037284L11.852187,49.227211C9.5922087,50.417148,8.1022395,49.327308,8.5321654,46.817066L10.052163,37.947467C10.482089,35.427454,9.3422125,31.917213,7.5121597,30.137434L1.0723097,23.857181C-0.75774248,22.077402,-0.18768217,20.317153,2.3423117,19.957292L11.242088,18.657492C13.772081,18.29763,16.752021,16.117462,17.882008,13.837196L21.861934,5.7673987C22.426928,4.6223834,23.171943,4.0498755,23.916957,4.0498749z M38.745234,0C39.49026,0,40.235289,0.57005358,40.800307,1.710161L44.780455,9.7809186C45.910495,12.071133,48.900607,14.241336,51.420699,14.611372L60.331027,15.901493C62.851119,16.271527,63.421141,18.031692,61.591071,19.81186L55.150836,26.092449C53.320769,27.872615,52.180728,31.382946,52.610743,33.903183L54.130797,42.764015C54.570815,45.284252,53.070758,46.364353,50.810675,45.184242L42.850383,40.993846C42.460368,40.783828,42.010352,40.633815,41.540336,40.493801L41.300326,39.093669C40.870311,36.583435,42.010352,33.063102,43.840419,31.292938L53.800788,21.582025C55.630854,19.801858,55.060832,18.041693,52.530742,17.671659L38.770232,15.671471C36.240142,15.301436,33.250029,13.131233,32.129989,10.851018L32.089988,10.771011C32.329997,10.450981,32.540004,10.12095,32.710009,9.7809186L36.690158,1.710161C37.255179,0.57005358,38.000205,0,38.745234,0z» Stretch=»Uniform» Fill=»#FFFFFFFF» Width=»30″ Height=»30″ Margin=»0,0,0,0″ />
  10.                         </Grid>
  11.                     </Viewbox>
  12.                     <TextBlock Text=»favorites» HorizontalAlignment=»Center»></TextBlock>
  13.                 </StackPanel>
  14.             </Button>
  15.         </StackPanel>
  16.     </AppBar>
  17. </Page.BottomAppBar>

Tenemos que tener en cuenta que el AppBar solo puede contener directamente un elemento, por esto usamos otros elementos contenedores, como un StackPanel o una Grid para colocar todos nuestros botones.

Orientación de la aplicación

Tras ver la barra de aplicación, otro detalle visual muy importante en las aplicaciones metro es la orientación de la pantalla y como respondemos a ella. Ya no estamos desarrollando para un ordenador portátil o de escritorio solamente, en las aplicaciones metro nos encontraremos en muchos casos, quizás la mayoría, ejecutándonos en tablets de cualquier tipo. La mayoría de estas tablets tendrán  acelerómetros que permitirán al usuario variar la vista del sistema girando el dispositivo. Pasando de una vista horizontal (landscape) o otra vertical (portrait).

En Windows Phone tenemos soporte para estos modos, pero podíamos forzar en nuestra página el modo que deseábamos soportar y olvidarnos del resto. En las aplicaciones metro, tenemos que tener todos los modos presentes, nuestra aplicación debe responder correctamente a los cambios de orientación.

Visual States al rescate

En principio esto parece una tarea complicada, controlar todo el layout de nuestra página y modificarlo dependiendo de la orientación del dispositivo. Pero existe un mecanismo que nos facilitará mucho este trabajo: los Visual States. En Windows Phone o en Silverlight 4, cuando creamos un template para un control, una de las acciones que tenemos a nuestra disposición son los Visual States, que nos permiten cambiar la apariencia de elementos de la template dependiendo de acciones o estados del control como por ejemplo pulsado, deshabilitado, normal, con foco…

Bien, en nuestras aplicaciones metro tendremos un grupo de Visual States que nos indicará el layout en el que se encuentra en cada momento. Este grupo se compone de cuatro estados distintos: FullScreenLandscape, FullScreenPortrait, Filled y Snapped:

Layout VisualStates
  1. <Grid Background=»{StaticResource ApplicationPageBackgroundBrush}«>
  2.     <VisualStateManager.VisualStateGroups>
  3.         <VisualStateGroup>
  4.             <VisualState x:Name=»FullScreenLandscape»>
  5.             </VisualState>
  6.             <VisualState x:Name=»Filled»>
  7.             </VisualState>
  8.             <VisualState x:Name=»FullScreenPortrait»>
  9.             </VisualState>
  10.             <VisualState x:Name=»Snapped»>
  11.             </VisualState>
  12.         </VisualStateGroup>
  13.     </VisualStateManager.VisualStateGroups>
  14. </Grid>

Cada uno representa un posible estado de nuestra pantalla: FullScreenLandscape representa una orientación horizontal del dispositivo y nuestra aplicación a pantalla completa, FullScreenPortrait representa una orientación vertical del dispositivo y nuestra aplicación a pantalla completa, Filled indica una orientación horizontal y nuestra aplicación ocupando la mayor parte de la pantalla y dejando una pequeña parte para otra aplicación y por último Snapped indica nuestra aplicación en esa pequeña parte dejada por una aplicación en modo Filled.

Una vez que hemos llegado a este punto, solo nos queda empezar a trabajar duro en hacer que nuestro layout cambie teniendo en cuenta estos estados. Vamos a empezar por crear un layout simple donde mostremos alguna imágen y texto:

image

Como podéis ver he creado alguna row y column más de las que necesito, esto es para tener una mayor flexibilidad de movimiento al cambiar entre los distintos VisualStates. Por defecto así sería nuestra aplicación, una bonita imagen y un buen texto sacado de Samuel Ipsum. Pero que pasa si cambiamos la orientación, por ejemplo a vertical? Pues que la imagen seguiría en el mismo sitio y el texto, pero al tener menos espacio horizontal se vería todo muy pequeño. Vamos a animar los estados de la página, abriendo la pestaña States de Expression Blend y seleccionando FullScreenPortrait, también, para tener más referencias visuales podemos ir a la pestaña de plataforma y cambiar la orientación a Portrait. Lo que voy a hacer es cambiar las columns y rows a las que está añadido cada elemento para aprovechar mejor el espacio en vertical:

image

Y que se ha generado por debajo en nuestro XAML? Muchas animaciones:

FullScreenPortrait
  1. <VisualState x:NameFullScreenPortrait«>
  2.     <Storyboard>
  3.         <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty(Grid.RowSpan)« Storyboard.TargetNameimage«>
  4.             <DiscreteObjectKeyFrame KeyTime0«>
  5.                 <DiscreteObjectKeyFrame.Value>
  6.                     <x:Int32>2</x:Int32>
  7.                 </DiscreteObjectKeyFrame.Value>
  8.             </DiscreteObjectKeyFrame>
  9.         </ObjectAnimationUsingKeyFrames>
  10.         <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty(Grid.ColumnSpan)« Storyboard.TargetNameimage«>
  11.             <DiscreteObjectKeyFrame KeyTime0«>
  12.                 <DiscreteObjectKeyFrame.Value>
  13.                     <x:Int32>3</x:Int32>
  14.                 </DiscreteObjectKeyFrame.Value>
  15.             </DiscreteObjectKeyFrame>
  16.         </ObjectAnimationUsingKeyFrames>
  17.         <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty(Grid.Row)« Storyboard.TargetNametextBlock«>
  18.             <DiscreteObjectKeyFrame KeyTime0«>
  19.                 <DiscreteObjectKeyFrame.Value>
  20.                     <x:Int32>2</x:Int32>
  21.                 </DiscreteObjectKeyFrame.Value>
  22.             </DiscreteObjectKeyFrame>
  23.         </ObjectAnimationUsingKeyFrames>
  24.         <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty(Grid.Column)« Storyboard.TargetNametextBlock«>
  25.             <DiscreteObjectKeyFrame KeyTime0«>
  26.                 <DiscreteObjectKeyFrame.Value>
  27.                     <x:Int32>0</x:Int32>
  28.                 </DiscreteObjectKeyFrame.Value>
  29.             </DiscreteObjectKeyFrame>
  30.         </ObjectAnimationUsingKeyFrames>
  31.         <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty(Grid.ColumnSpan)« Storyboard.TargetNametextBlock«>
  32.             <DiscreteObjectKeyFrame KeyTime0«>
  33.                 <DiscreteObjectKeyFrame.Value>
  34.                     <x:Int32>3</x:Int32>
  35.                 </DiscreteObjectKeyFrame.Value>
  36.             </DiscreteObjectKeyFrame>
  37.         </ObjectAnimationUsingKeyFrames>
  38.         <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty(FrameworkElement.Margin)« Storyboard.TargetNametextBlock«>
  39.             <DiscreteObjectKeyFrame KeyTime0«>
  40.                 <DiscreteObjectKeyFrame.Value>
  41.                     <Thickness>50,-20,50,50</Thickness>
  42.                 </DiscreteObjectKeyFrame.Value>
  43.             </DiscreteObjectKeyFrame>
  44.         </ObjectAnimationUsingKeyFrames>
  45.     </Storyboard>
  46. </VisualState>

Simplemente hemos tomado el trabajo de planificar antes de comenzar a diseñar que podríamos necesitar para colocar nuestra información y a continuación hemos realizado los cambios necesarios con Blend para que se adapte nuestra aplicación a la resolución, el resultado? Al ejecutar nuestra aplicación no funcionan las animaciones que hemos creado, no cambia el estado… Esto es porque necesitamos indicarle a nuestra página a que estado debe ir cuando la orientación o posición cambien, simplemente en el código de nuestra página, usando el namespace Windows.UI.ViewManagement obtendremos el ApplicationView que nos devolverá en todo momento el ViewState en el que nos encontremos. Lo único que necesitamos es subscribirnos al evento SizeChanged de nuestra ventana y usar el método GoToState del VisualStateManager:

Ahora sí, si ejecutamos veremos como nuestra aplicación reacciona a las diferentes orientaciones y estados:

image

Conclusión

En este séptima entrega de la serie hemos podido ver dos de las novedades en el apartado de layout que nos ofrece metro en comparación con Windows Phone, simplemente teniéndolas en cuenta, mejoraremos la experiencia de usuario de nuestra aplicación en gran medida y por fin hemos empezado a ver algo más que Visual Studio, a utilizar el nuevo Expression Blend. Como siempre, aquí os dejo los ejemplos del artículo para que podáis jugar con ellos y tenerlos como referencia. Nos vemos en el siguiente artículo!

Un saludo y Happy Coding!

Cambiando el VisualState
  1. public BlankPage()
  2. {
  3.     this.InitializeComponent();
  4.  
  5.     Window.Current.SizeChanged += Current_SizeChanged;
  6.     VisualStateManager.GoToState(this, ApplicationView.Value.ToString(), true);
  7. }
  8.  
  9. void Current_SizeChanged(Object sender, Windows.UI.Core.WindowSizeChangedEventArgs e)
  10. {
  11.     VisualStateManager.GoToState(this, ApplicationView.Value.ToString(), true);
  12. }

[Windows Phone 7.5] Tip: Cookies HttpOnly en HttpWebRequest y depurar peticiones web

Hola a todos!

Estos días jugando con HttpWebRequest para una aplicación que necesitaba consumir unos servicios REST me encontré con un pequeño problema cuya solución no es demasiado obvia, así que os la traigo por si estáis en la misma situación en algún momento.

Resulta que estos servicios requerían autenticación, devolviendo un token de sesión que tienes que usar en el resto de llamadas pero… sorpresa: el token se devolvía como una cookie HttpOnly, con lo que necesitaba acceder a la misma para conservar el token y reutilizarlo en las siguientes llamadas.

Para empezar, si queremos trabajar con cookies y con la clase HttpWebRequest, tenemos que usar la pila Http de cliente y no del navegador, indicándolo mediante el método RegisterPrefix de HttpWebRequest:

RegisterPrefix
  1.  
  2. bool httpResult = HttpWebRequest.RegisterPrefix(«http://», WebRequestCreator.ClientHttp);

De esta forma, en nuestro HttpWebRequest podremos usar un CookieContainer que nos será de utilidad más adelante.

Ahora solo tenemos que crear la llamada y al recibir la respuesta, obtener la colección de cookies disponibles y extraer la que nos interesa:

Request / Response
  1. HttpWebRequest request;
  2. CookieContainer cookies = new CookieContainer();
  3.  
  4. bool httpResult = HttpWebRequest.RegisterPrefix(«http://», WebRequestCreator.ClientHttp);
  5.  
  6. request = (HttpWebRequest)WebRequest.Create(«http://miwebservice/method»);
  7. request.Headers[«Accept-Language»] = «es-ES»;
  8. request.Accept = «text/html, application/xhtml+xml, */*»;
  9. request.Method = «GET»;
  10.  
  11. request.BeginGetResponse(new AsyncCallback((ar) =>
  12. {
  13.     HttpWebRequest Request = (HttpWebRequest)ar.AsyncState;
  14.     if (Request != null)
  15.     {
  16.         using (HttpWebResponse webResponse = (HttpWebResponse)Request.EndGetResponse(ar))
  17.         {
  18.             var session = webResponse.Cookies[«SESSIONID»];
  19.         }
  20.     }
  21. }), request);

Pero aquí nos encontramos con un muro, aunque la cookie SESSIONID está llegando correctamente si usamos una aplicación como Fiddler para examinar las peticiones y respuestas HTTP, la colección Cookies de nuestro HttpWebResponse no contiene la misma. Al examinar la respuesta veo que efectivamente la cookie está allí por lo que se tiene que tratar de un problema al recibirla.

Después de mucha prueba y error, basándome en que una de las características de las cookies HttpOnly es que no pueden ser accedidas desde scripts se me ocurrió que quizás la cookie si estaba siendo recibida, simplemente no podía verla… ¿que pasa si hacemos lo siguiente?:

Response with HttpOnly cookie
  1. cookies.Add(new Uri(«http://miwebservice»), webResponse.Cookies);
  2. request = (HttpWebRequest)WebRequest.Create(«http://miwebservice/method2»);
  3. request.CookieContainer = cookies;
  4. request.BeginGetResponse(new AsyncCallback((asyncResult) =>
  5. {
  6.     Request = (HttpWebRequest)asyncResult.AsyncState;
  7.     if (Request != null)
  8.     {
  9.         using (HttpWebResponse resp = (HttpWebResponse)Request.EndGetResponse(asyncResult))
  10.         {
  11.  
  12.         }
  13.     }
  14. }), request);

Añado la colección de cookies que me ha devuelto mi petición a un CookieContainer y en las siguientes peticiones asigno a la propiedad CookieContainer de HttpWebRequest el contenedor con las cookies que me ha devuelto mi primera petición… y voilá! efectivamente, la cookie HttpOnly se encontraba allí, solo que no podía acceder a ella.

Lo único que necesitamos es guardarnos la colección de cookies devuelta y tendremos todo lo que necesitamos. No es que me haga demasiada ilusión no poder trabajar directamente con la cookie, comprobar si realmente existe, etc… pero es la única forma que hay de poder usarla, así que tendremos que conformarnos.

Fiddler2

Para todo este trabajo, la ayuda de un depurador web como Fiddler se hace indispensable, te permite ver que están haciendo tus peticiones exactamente y extraer todos los datos que necesites. Si lo instalas con  el enlace que he puesto más arriba, al iniciarlo no recibirá ninguna petición desde el emulador de Windows Phone, tenemos que hacer unos pocos pasos para que funcione correctamente:

  1. Iniciar Fiddler 2 (v2.3.0.7 o superior) e ir a Tools > Fiddler Options
  2. Ir a la pestaña Connections y marchar el check Allow remote computers to connect
  3. Presionamos OK y reiniciamos la aplicación
  4. En la pantalla inicial, abajo a la izquierda tenemos una caja de texto negra, escribir en ella: prefs set fiddler.network.proxy.registrationhostname HostName donde HostName es el nombre de tu PC.
  5. Reinicia tanto el emulador como la aplicación y abre internet explorer en el emulador, verás el trafico en Fiddler

Con estos sencillos pasos, tendremos una forma sencilla de ver el tráfico de nuestras aplicaciones y que está pasando en concreto. En el caso que os explico al hacer la petición al método de autenticación pude ver que la cookie era HttpOnly:

fiddler2

Vemos que la respuesta nos devuelve una cabecera Set-Cookie con el parámetro HttpOnly (Abajo, recuadro rojo). También es interesante poder ver que aplicación del dispositivo a realizado la petición si examinamos el Referer de la petición (Arriba, recuadro azul). Al mirar que efecto tenía al pasar la colección de cookies devuelta directamente a otra petición pude ver gracias a Fiddler que en la petición se estaba incluyendo la cookie correctamente:

fiddler2_2

Y esto es todo lo que os quería contar hoy, espero que os sea útil, tanto para no volveros locos buscando la Cookie perdida como para aprovecharos de Fiddler en vuestros desarrollos.

Un saludo y Happy Coding!

[Windows 8] Apps Metro para desarrolladores Windows Phone 7.5 (6 de N)

Hola a todos!

Continuando con nuestra serie sobre Windows 8 para desarrolladores Windows Phone 7.5, hoy vamos a examinar una parte muy importante de toda aplicación: La localización. Todos sabemos ya que ofertar nuestra aplicación en diferentes idiomas puede marcar una gran diferencia en las descargas y el uso de la misma. Hace algún tiempo ya vimos lo fácil que era realizar esto en Windows Phone 7.5 (aquí), en las aplicaciones Metro es, si cabe, aún más sencillo. Simplemente deberemos seguir algunas convenciones y automáticamente se obrará la magia de obtener una aplicación con soporte para múltiples idiomas.

Archivos RESX y Archivos RESW

En Windows Phone (y en Silverlight o WPF) disponíamos de archivos de recursos, del tipo RESX. En Windows 8 tendremos un nuevo tipo de archivo de recursos: RESW. Estos nuevos archivos RESW comparten la forma de trabajar de los archivos RESX, tendremos un editor donde podremos crear pares de clave/valor que representen recursos:

image

Pero aquí terminan las similitudes. Con los archivos RESX, se generaba una clase automáticamente con las propiedades que hubiésemos indicado. En el caso de los RESW, estos son compilados a un formato binario PRI y no se genera ninguna clase a la que podamos acceder mediante código, lo cual tampoco es necesario.

Y aquí comienzan las convenciones. El archivo RESW debe llamarse Resources.resw, no sé si esto se trata de una limitación de la beta actual o esta convención estará presente en la versión final de las herramientas, pero por ahora es así. Podemos colocar el archivo en cualquier carpeta de nuestra solución, pero obligatoriamente deberemos contenerlo en una subcarpeta con el nombre del idioma que soportamos. De esta forma, si nuestra aplicación soportase Inglés, Español y Francés, tendríamos esta estructura de directorios:

image

Otra notable diferencia que encontramos entre los archivos RESX y los RESW es la forma de utilizarlos. En este sentido, los archivos RESW se parecen mucho a la forma de traducir los textos de ASP.NET. Como nombre del valor indicaremos un nombre que luego asignaremos a una propiedad del control a traducir y también indicaremos la propiedad a la que deseamos aplicar el valor, por ejemplo: AppTitle.Text:

image

Ahora en el elemento o elementos de nuestro XAML en los que deseemos usar este recurso, deberemos indicar la propiedad x:Uid con el valor AppTitle:

BlankPage.xaml
  1. <Grid Background=»{StaticResource ApplicationPageBackgroundBrush}«>
  2.     <TextBlock Margin=»110,20,20,0″ HorizontalAlignment=»Left»
  3.                 VerticalAlignment=»Top» FontSize=»56″
  4.                 x:Uid=»AppTitle»
  5.                 Text=»AppTitle»></TextBlock>
  6. </Grid>

Si ejecutamos nuestra aplicación de ejemplo y tenéis el idioma de Windows 8 establecido en Inglés, obtendremos automáticamente el siguiente resultado:

image

Se ha hecho la magia! Automáticamente la aplicación a buscado un recuso con el identificador que hemos indicado en la propiedad x:Uid y a aplicado el valor encontrado a la propiedad indicada, teniendo en cuenta el idioma actual para seleccionar el recurso correcto. Si cambiásemos el idioma de Windows a Español o Francés el resultado cambiaría acorde:

imageimage

¿Como identifica el idioma Windows si tenemos varios instalados? Si vamos a la lista de idiomas en Control Panel > Languages:

image

Nuestra aplicación usará como idioma el primero de la lista, siempre que exista más de uno. De esta forma, variando el orden de esta lista podremos probar diferentes configuraciones de idiomas.

Imágenes

Otro punto de localización que podemos encontrar son las imágenes, en muchas aplicaciones queremos cambiar una imagen que mostramos dependiendo del idioma en que estemos trabajando. Para realizar esto lo primero que tenemos que hacer es organizar nuestras imágenes de la misma forma que nuestros recursos RESW, en carpetas por la cultura a la que pertenecen:

image

A continuación usaremos el formato de Uri ms-appx:/// para referenciar a nuestra imagen, sin indicar el subdirectorio del idioma en el que se encuentra, de esta forma:

Localización de imágenes
  1. <Image VerticalAlignment=»Top» HorizontalAlignment=»Left»
  2.         Width=»300″ Margin=»110,100,20,0″
  3.         Source=»ms-appx:///images/map.gif»></Image>

Automáticamente WinRT nos dará la imagen que se encuentre dentro de la carpeta de idioma correspondiente al idioma actual:

image

Conclusión

Como hemos podido ver, la localización de aplicaciones en aplicaciones Metro es realmente sencilla, lo cual hace que no haya excusa para traducir una aplicación: Aumentará nuestro éxito de forma exponencial y no aumentará nuestro tiempo de desarrollo. De hecho, usando este sistema, podemos corregir fallos de expresión o ortográficos de una forma sencilla y compartir una misma cadena de texto para varios elementos, con lo que modificarla sería mucho más sencillo que si el texto estuviese diseminado por todo el XAML. Como siempre, aquí tenéis el proyecto de ejemplo que he usado, aunque esta vez es realmente sencillo, para que juguéis con el. Pronto una nueva entrega, estad atentos!

Un saludo y Happy Coding!