[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 ApplicationPageBackgroundBrush}">
  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:Name="FullScreenPortrait">
  2.     <Storyboard>
  3.         <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(Grid.RowSpan)" Storyboard.TargetName="image">
  4.             <DiscreteObjectKeyFrame KeyTime="0">
  5.                 <DiscreteObjectKeyFrame.Value>
  6.                     <x:Int32>2</x:Int32>
  7.                 </DiscreteObjectKeyFrame.Value>
  8.             </DiscreteObjectKeyFrame>
  9.         </ObjectAnimationUsingKeyFrames>
  10.         <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(Grid.ColumnSpan)" Storyboard.TargetName="image">
  11.             <DiscreteObjectKeyFrame KeyTime="0">
  12.                 <DiscreteObjectKeyFrame.Value>
  13.                     <x:Int32>3</x:Int32>
  14.                 </DiscreteObjectKeyFrame.Value>
  15.             </DiscreteObjectKeyFrame>
  16.         </ObjectAnimationUsingKeyFrames>
  17.         <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(Grid.Row)" Storyboard.TargetName="textBlock">
  18.             <DiscreteObjectKeyFrame KeyTime="0">
  19.                 <DiscreteObjectKeyFrame.Value>
  20.                     <x:Int32>2</x:Int32>
  21.                 </DiscreteObjectKeyFrame.Value>
  22.             </DiscreteObjectKeyFrame>
  23.         </ObjectAnimationUsingKeyFrames>
  24.         <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(Grid.Column)" Storyboard.TargetName="textBlock">
  25.             <DiscreteObjectKeyFrame KeyTime="0">
  26.                 <DiscreteObjectKeyFrame.Value>
  27.                     <x:Int32>0</x:Int32>
  28.                 </DiscreteObjectKeyFrame.Value>
  29.             </DiscreteObjectKeyFrame>
  30.         </ObjectAnimationUsingKeyFrames>
  31.         <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(Grid.ColumnSpan)" Storyboard.TargetName="textBlock">
  32.             <DiscreteObjectKeyFrame KeyTime="0">
  33.                 <DiscreteObjectKeyFrame.Value>
  34.                     <x:Int32>3</x:Int32>
  35.                 </DiscreteObjectKeyFrame.Value>
  36.             </DiscreteObjectKeyFrame>
  37.         </ObjectAnimationUsingKeyFrames>
  38.         <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.Margin)" Storyboard.TargetName="textBlock">
  39.             <DiscreteObjectKeyFrame KeyTime="0">
  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 y nos subscribiremos al evento ViewStateChanged, cuando se lance este evento solo tendremos que llamar al método GoToState de la clase ViewStateManager para indicarle el nuevo Visual State:

Cambiando el VisualState
  1. public BlankPage()
  2. {
  3.     this.InitializeComponent();
  4.  
  5.     ApplicationView.GetForCurrentView().ViewStateChanged += BlankPage_ViewStateChanged;
  6.     VisualStateManager.GoToState(this, ApplicationView.Value.ToString(), true);
  7. }
  8.  
  9. void BlankPage_ViewStateChanged(ApplicationView sender, ApplicationViewStateChangedEventArgs args)
  10. {
  11.     VisualStateManager.GoToState(this, args.ViewState.ToString(), true);
  12. }

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!

[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!

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

Hola a todos!

En esta quinta entrega de la serie, tras haber visto como funcionan los contratos en las aplicaciones metro, vamos a profundizar un poco más con tres contratos muy útiles: buscar, compartir y configuración. Cada uno de estos contratos va a dar una gran funcionalidad a nuestra aplicación e integrarla con la forma de trabajar de Windows 8. Empecemos!

Search

El contrato de búsqueda permite que el usuario consulte a nuestra aplicación desde cualquier parte del sistema:

image

En esta pantalla, además de buscar aplicaciones, configuraciones o archivos, podemos buscar dentro de las propias aplicaciones listadas a la derecha. Si nuestra aplicación soporta el contrato de búsqueda vendrá listada aquí también.  Si por ejemplo escogemos la aplicación Weather y buscamos una ciudad, recibiremos resultados que podremos escoger:

image

¿Qué necesitamos para comenzar? En primer lugar, como recordaréis del último artículo, necesitamos indicar en las declaraciones de nuestra aplicación (package.appxmanifest) que deseamos soportar el contrato de búsqueda. Lo siguiente es que en nuestra clase App controlemos la activación mediante una búsqueda, con el método OnSearchActivated, lo que vamos a hacer en este caso es llamar a un método de nuestra página que recogerá el texto introducido por el usuario y lo mostrará en pantalla:

BlankPage.xaml.cs
  1. public void SetSearchQuery(string query)
  2. {
  3.     txtSearchQuery.Text = string.Format("Search query:{0}", query);
  4. }

 

App.xaml.cs
  1. protected override void OnSearchActivated(SearchActivatedEventArgs args)
  2. {
  3.     base.OnSearchActivated(args);
  4.  
  5.     if (Window.Current.Content == null)
  6.     {
  7.         Window.Current.Content = new BlankPage();
  8.         Window.Current.Activate();
  9.     }
  10.  
  11.     ((BlankPage)Window.Current.Content).SetSearchQuery(args.QueryText);
  12. }

Si realizamos un despliegue de nuestra aplicación y a continuación abrimos la búsqueda de Windows, veremos que ya aparece listada y que al seleccionarla se activa y muestra el texto que hayamos indicado. Pero no hace mucho más, no nos ofrece sugerencias como la aplicación del tiempo… vamos a solucionar esto desde nuestra página BlankPage.xaml.

En primer lugar necesitamos añadir un using al namespace Windows.ApplicationModel.Search y a continuación crearemos dos variables privadas en nuestra clase, una será lista con distintas palabras y la otra almacenará la referencia al panel de búsqueda:

Variables
  1. private SearchPane searchPane;
  2. private List<string> list = new List<string>() { "prueba", "yeray", "julin", "Ferreiro", "Plain Concepts" };

Ahora, en el constructor vamos a obtener la instancia actual de la búsqueda y manejar el evento SuggestionsRequested que nos llegará cuando el usuario escriba en la caja de búsqueda:

Constructor
  1. public BlankPage()
  2. {
  3.     this.InitializeComponent();
  4.  
  5.     this.searchPane = SearchPane.GetForCurrentView();
  6.     this.searchPane.SuggestionsRequested += searchPane_SuggestionsRequested;
  7. }

De esta forma, cada vez que el usuario escriba en la caja de búsqueda, nos llegará la llamada al evento SuggestionsRequested y podremos obtener la consulta del usuario y añadir las sugerencias que coincidan (usando nuestro propio criterio de coincidencia) al panel de sugerencias:

SuggestionsRequested
  1. void searchPane_SuggestionsRequested(SearchPane sender, SearchPaneSuggestionsRequestedEventArgs args)
  2. {
  3.     args.Request.SearchSuggestionCollection.AppendQuerySuggestions(list.FindAll(i => i.Contains(args.QueryText)).ToList());
  4. }

También disponemos de más métodos para añadir solo una sugerencia o, por ejemplo, añadir un texto de separación usando el método AppendSearchSeparator:

AppendSearchSeparator
  1. void searchPane_SuggestionsRequested(SearchPane sender, SearchPaneSuggestionsRequestedEventArgs args)
  2. {
  3.     args.Request.SearchSuggestionCollection.AppendQuerySuggestions(list.FindAll(i => i.Contains(args.QueryText)).ToList());
  4.     args.Request.SearchSuggestionCollection.AppendSearchSeparator("Another sugestions");
  5.     args.Request.SearchSuggestionCollection.AppendQuerySuggestions(list.FindAll(i => i.Contains(args.QueryText)).ToList());
  6. }

El resultado, podemos mostrar sugerencias, obtener las búsquedas y reaccionar de forma adecuada:

image

Share

El contrato de Share, compartir, nos permite intercambiar información entre aplicaciones. En Windows Phone 7.5 teníamos dos lanzadores, ShareStatusTask y ShareLinkTask, que nos permitían compartir texto o enlaces en las redes sociales configuradas en el sistema. En Windows 8 el contrato Share tiene un sentido mucho más amplio. Podemos hacer que nuestra aplicación sea una fuente de información y que otras aplicaciones puedan recoger esa información y usarla. De la misma forma podemos declarar que somos capaces e utilizar cierto tipo de información, de esa forma cuando alguna aplicación comparta, por ejemplo textos o imágenes, apareceremos en la lista de posibles destinos.

Esta operación de compartir información, como ya hemos comentado, consta de dos partes: la fuente de datos y el destino al que compartir. Veamos a continuación como implementar cada uno:

Share source

Nuestra aplicación puede contener información que el usuario quiera tratar, compartir, editar o modificar en otra aplicación. Por ejemplo, podemos tener una aplicación de Rss sobre noticias y el usuario puede tener una aplicación donde organiza todos los artículos que le parecen interesantes, podemos compartir las Urls de los artículos y que otras aplicaciones los recojan.

¿Como podemos hacerlo? Muy sencillo, lo primero que necesitamos es prepararnos para poder transferir datos, obteniendo una instancia de la clase DataTransferManager del namespace Windows.ApplicationModel.DataTransfer y manejando el evento DataRequested para cuando otra aplicación nos solicite datos:

DataTransferManager
  1. DataTransferManager datatransferManager;
  2. datatransferManager = DataTransferManager.GetForCurrentView();
  3. datatransferManager.DataRequested += datatransferManager_DataRequested;

A continuación vamos a crear la interface de usuario que nos permita compartir un texto introducido por el usuario:

XAML
  1. <Grid Background="{StaticResource ApplicationPageBackgroundBrush}">
  2.     <Grid Margin="110,20,20,20">
  3.         <Grid.RowDefinitions>
  4.             <RowDefinition Height="Auto"></RowDefinition>
  5.             <RowDefinition></RowDefinition>
  6.         </Grid.RowDefinitions>
  7.             
  8.         <TextBlock Text="Windows 8 share source" FontSize="36"></TextBlock>
  9.             
  10.         <StackPanel Grid.Row="1" Margin="0,12,0,0">
  11.             <TextBlock Text="Texto a compartir" FontSize="16"></TextBlock>
  12.             <TextBox Name="txtText" Height="100" TextWrapping="Wrap" AcceptsReturn="True" Margin="0,12,0,12"></TextBox>
  13.             <Button Content="Compartir" Click="Button_Click_1"></Button>
  14.         </StackPanel>
  15.     </Grid>
  16. </Grid>

En el evento del botón compartir mostramos la UI de compartir:

Button_click
  1. private void Button_Click_1(object sender, RoutedEventArgs e)
  2. {
  3.     DataTransferManager.ShowShareUI();
  4. }

Esto provocará que el sistema lance el evento DataRequested para identificar el tipo de datos que vamos a compartir y otras propiedades como el título y la descripción de la información que estamos compartiendo:

DataRequested
  1. void datatransferManager_DataRequested(DataTransferManager sender, DataRequestedEventArgs args)
  2. {
  3.     args.Request.Data.Properties.Title = "Compartiendo texto";
  4.     args.Request.Data.Properties.Description = "Ejemplo de como usar el contrato Share";
  5.     args.Request.Data.SetText(this.txtText.Text);
  6. }

Si ejecutamos nuestra aplicación, escribimos un texto y presionamos el botón compartir, aparecerá a la derecha una lista de las aplicaciones que pueden trabajar con la información que hemos expuesto:

image

Si a continuación seleccionamos la aplicación de correo, aparecerá directamente la vista de envío de un nuevo correo, con los datos que hemos indicado al compartir introducidos:

image

Como podemos observar es realmente sencillo usar la comunicación entre aplicaciones. Pero no solo podemos compartir texto, también podemos compartir imágenes o cualquier otro tipo de datos. Vamos a usar un FileOpenPicker, del namespace Windows.Storage.Pickers  para poder seleccionar una imagen de nuestro pc y compartirla:

Obtener una imágen
  1. FileOpenPicker imagePicker = new FileOpenPicker
  2. {
  3.     ViewMode = PickerViewMode.Thumbnail,
  4.     SuggestedStartLocation = PickerLocationId.PicturesLibrary,
  5.     FileTypeFilter = { ".jpg", ".png", ".bmp", ".gif", ".tif" }
  6. };
  7.  
  8. this.imageFile = await imagePicker.PickSingleFileAsync();

En el evento DataRequested podemos usar el método SetBitmap para establecer la imagen a compartir:

SetBitmap
  1. args.Request.Data.SetBitmap(RandomAccessStreamReference.CreateFromFile(this.imageFile));

Pero la aplicación de correo no es capaz de compartir imágenes, por lo que no aparecerá ninguna aplicación disponible, no hay problema, vamos a ver como podemos crear una aplicación que pueda recibir imágenes o texto de forma indistinta.

Share target

Ahora que ya sabemos como hacer que nuestra aplicación se comunique con el resto del sistema usando las capacidades de compartir, veamos como podemos recibir información de otras aplicaciones, exponiéndonos como destino de los datos. Lo primero que necesitamos hacer es añadir a nuestra aplicación el contrato de Share Target y los tipos de datos que soportamos, editando la configuración del archivo Package.appxmanifest en este caso usaremos los tipos de datos de texto e imágenes:

image

Una vez añadidos estos tipos de datos que deseamos soportar, vamos a editar nuestro archivo App.xaml.cs para que soporte la activación de nuestra aplicación mediante el contrato Share, la entrada se realizará por el método OnShareTargetActivated:

OnShareTargetActivated
  1. protected async override void OnShareTargetActivated(ShareTargetActivatedEventArgs args)
  2. {
  3.     BlankPage shareTargetPage = new BlankPage();
  4.     await shareTargetPage.ShowData(args);
  5. }

Lo que hemos hecho es cargar nuestra página principal, aunque podría ser una página especial para cuando recibamos datos a compartir, y llamar a un método de esta página que hemos creado y será el encargado de cargar y mostrar todos los datos recibidos, el método ShowData:

ShowData
  1. public async Task ShowData(ShareTargetActivatedEventArgs args)
  2. {
  3.     txtApp.Text = args.ShareOperation.Data.Properties.ApplicationName;
  4.     txtTitle.Text = args.ShareOperation.Data.Properties.Title;
  5.     txtDescription.Text = args.ShareOperation.Data.Properties.Description;
  6.     txtText.Text = await args.ShareOperation.Data.GetTextAsync();
  7.  
  8.     IRandomAccessStreamReference imageReceived = await args.ShareOperation.Data.GetBitmapAsync();
  9.     IRandomAccessStreamWithContentType stream = await imageReceived.OpenReadAsync();
  10.     BitmapImage bitmapImage = new BitmapImage();
  11.     bitmapImage.SetSource(stream);
  12.     img.Source = bitmapImage;
  13.  
  14.     Window.Current.Content = this;
  15.     Window.Current.Activate();
  16. }

Igual que al compartir usamos los métodos SetBitmap y SetText, ahora usamos sus homónimos GetTextAsync y GetBitmapAsync, lo que nos permite obtener la imagen seleccionada por el usuario en la otra aplicación y el texto que escribió y mostrarlo en pantalla:

image

Como podemos ver, es realmente sencillo comunicar aplicaciones entre sí usando el contrato Share, además mantenemos nuestras aplicaciones independientes, pues lo único que necesitamos exponer es el tipo de datos que soportamos, nada más.

Settings

Por regla general, todas las aplicaciones tienen alguna opción que configurar, siempre hemos tenido que crear una pantalla de configuración para permitir al usuario personalizar su experiencia. En Windows 8 podemos integrar este trabajo con la barra de charms, teniendo las configuraciones de nuestra aplicación en el mismo sitio que los settings del sistema. Por defecto, automáticamente tendremos una sección de permisos, donde el usuario podrá habilitar o deshabilitar las capacidades otorgadas a nuestra aplicación. Por ejemplo, si requerimos acceso al micrófono o la localización:

image

De esta forma nos ahorramos pantallas especiales, enlaces y demás, el propio sistema se encarga de todo y además el usuario sabe donde encontrar la configuración de nuestra aplicación en cualquier momento. Además de esto, que se realiza de forma automática, podemos crear nuestras propias secciones con su contenido personalizado e integrarlo en el mismo menú de settings.

Lo que vamos a hacer es registrar nuevos botones al lanzar nuestra aplicación, en primer lugar tenemos que obtener una referencia al panel de settings actual, usando el tipo SettingsPane del namespace Windows.UI.ApplicationSettings:

SettingsPane
  1. private async Task RegisterSettings()
  2. {
  3.     SettingsPane settings = SettingsPane.GetForCurrentView();
  4.     settings.CommandsRequested += settings_CommandsRequested;
  5. }

A continuación en el evento CommandsRequested vamos a crear nuestros comandos y agregarlos a la barra de settings:

CommandsRequested
  1. void settings_CommandsRequested(SettingsPane sender, SettingsPaneCommandsRequestedEventArgs args)
  2. {
  3.     SettingsCommand HelpCommand = new SettingsCommand("helpSetting", "Help", new UICommandInvokedHandler(onHelpCommand));
  4.     args.Request.ApplicationCommands.Add(HelpCommand);
  5.     SettingsCommand VideoCommand = new SettingsCommand("videoSettings", "Video", new UICommandInvokedHandler(onVideoCommand));
  6.     args.Request.ApplicationCommands.Add(VideoCommand);
  7. }

Para cada comando además definimos un método que ejecutará al seleccionarlo:

Código comandos
  1. public async void onHelpCommand(IUICommand command)
  2. {
  3.     SettingsCommand HelpCommand = (SettingsCommand)command;
  4.     MessageDialog dlg = new MessageDialog("Has seleccionado Help");
  5.     await dlg.ShowAsync();
  6. }
  7.  
  8. public async void onVideoCommand(IUICommand command)
  9. {
  10.     SettingsCommand VideoCommand = (SettingsCommand)command;
  11.     MessageDialog dlg = new MessageDialog("Has seleccionado Video");
  12.     await dlg.ShowAsync();
  13. }

Si ejecutamos nuestra aplicación, veremos las nuevas secciones y al seleccionar una se nos mostrará el texto indicado:

image

De esta forma podemos permitir que el usuario acceda a la configuración de nuestra aplicación desde un punto unificado.

Conclusión

Con esto, hemos terminado nuestro repaso a los contratos de Windows 8, hay muchos más, pero creo que habiendo visto en profundidad los contratos de busqueda, compartir y settings, el resto no nos darán problemas. Como siempre, os dejo los proyectos con el código completo de los ejemplos que hemos visto, podéis descargarlos aquí. Espero veros a todos en próximos artículos.

Un saludo y Happy Coding!

[MATERIALES] Evento AppCircus Sevilla Windows Phone 7.5

AppCircus Sevilla

Hola a todos!

El pasado jueves 19 de Abril estuve en tierras sevillanas impartiendo un workshop de introducción al desarrollo en Windows Phone 7.5 junto a Oscar Gutiérrez de Nokia Spain. Dimos un repaso a que es metro, MVVM, pruebas unitarias y desarrollamos una aplicación funcional para a continuación ver como usar herramientas como el Marketplace test kit y como subirla al App hub de Windows Phone.

Aquí os dejo el código de ejemplo de la aplicación que desarrollamos y a continuación las slides de la presentación:

Un saludo y Happy Coding!

[EVENTO] Windows Phone 7.5 en AppCircus Sevilla

Hola a todos!

AppCircus Sevilla

Este jueves 19 de Abril se celebrará en la ETSI Informática de la Universidad de Sevilla una nueva edición del concurso AppCircus. En esta ocasión estaré junto a Oscar Gutierrez de Nokia Spain presentando un workshop de desarrollo sobre Windows Phone 7.5.

Daremos un repaso rápido sobre:

  • Que es Metro
  • Las herramientas de desarrollo
  • El patrón MVVM
  • El Marketplace de Windows Phone

Intentaremos desarrollaremos una aplicación completa y veremos como podemos subirla al marketplace.

Os espero a las 10:30 el 19 de Abril para hablar un poco sobre Windows Phone y su modelo de desarrollo, no os lo perdáis.

Un saludo y Happy Coding!

Publicado por Josué Yeray Julián Ferreiro con no comments
Archivado en: ,,

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

Volvemos a la carga con un nuevo artículo de la serie!

En nuestro último artículo sobre el ciclo de vida de las aplicaciones Metro vimos como es posible activar nuestra aplicación en respuesta a un requerimiento del sistema (como una búsqueda) o a otras aplicaciones (presentándonos como objetivo para guardar archivos). Pero no profundizamos en como llevar a cabo estas tareas. En este artículo vamos a ver que son los contratos, como configurar nuestra aplicación para que indique que contratos soporta y como realizar comunicaciones entre aplicaciones.

Contratos

El concepto de contrato es nuevo en Windows 8, en Windows Phone 7.5 disponíamos de sistemas que nos permitía exponer nuestra aplicación como capaz de editar fotos mediante Picture extensibility o realizar búsquedas llamado App Connect. Los contratos obtienen esta idea y la extienden más allá, permitiendo nuevas formas de interacción.

Package.appxmanifest

Para que nuestra aplicación sea capaz de usar estas características, lo primero que tenemos que hacer es habilitar las que deseamos usar, esto lo conseguiremos haciendo doble click sobre el archivo Package.appxmanifest de nuestra solución, que nos permitirá configurar nuestra aplicación. Esta pantalla, se divide en cuatro secciones distintas: Application UI, Capabilities, Declarations y Packaging:

Application UI nos permite configurar detalles sobre la interface de usuario de nuestra aplicación: rotaciones soportadas, nombre, iconos, etc…

image

Capabilities sirve para indicar que capacidades del sistema puede usar nuestra aplicación, lo que en Windows Phone se configuraba manualmente en el archivo WMAppManifest.xml, ahora con una interface más agradable:

image

Es importante que tengamos en cuenta que estas capacidades indican a qué tendrá acceso nuestra aplicación, una vez instalada, si deseamos cambiar alguna capacidad tendremos que desinstalarla y volver a instalarla.

Packaging nos ofrece datos generales sobre el paquete que se genera al compilar nuestra aplicación, el nombre de paquete, logo, el certificado a usar:

image

Declarations, nos lo habíamos saltado para poder tratarlo en más profundidad, la pestaña Declarations nos permite establecer y configurar los contratos a los que responderá nuestra aplicación:

image

 

Un Ejemplo FileSavePicker

Podemos seleccionar un elemento de la lista de available declarations y añadirlo a las supported declarations, en ese momento nos pedirá toda la configuración que necesite para poder funcionar. Si por ejemplo añadimos el contrato File save picker veremos sus valores de configuración:

image

Podemos configurar el tipo de archivo soportado, indicando sus extensión o eliminar todos los tipos y marcar el checkbox de soportar cualquier tipo, caso en el cual nuestra aplicación aparecerá como destino para guardar en todas las demás aplicaciones Metro, como por ejemplo al guardar un PDF desde la aplicación Reader:

image

Como vimos en el capítulo anterior, cuando el usuario seleccione nuestra aplicación en la lista de destinos para guardar, se activará mediante el evento OnFileSavePickerActivated, donde podremos establecer la UX que queremos mostrar al usuario:

OnFileSavePickerActivated
  1. protected override void OnFileSavePickerActivated(FileSavePickerActivatedEventArgs args)
  2. {
  3.     var page = new BlankPage();
  4.     page.Activate(args);
  5. }

Y en el método Activate de nuestra página, acceder al FileSavePickerActivatedEventArgs para obtener la información necesaria y mostrar la interface:

Activación de la página
  1. public void Activate(FileSavePickerActivatedEventArgs args)
  2. {
  3.     txtFile.Text = args.FileSavePickerUI.FileName;
  4.     Window.Current.Content = this;
  5.     Window.Current.Activate();
  6. }

Si realizamos un Deploy de nuestra aplicación y a continuación abrimos un pdf por ejemplo, podremos ver los resultados:

image

 

¿Como saber cuando el usuario a presionado el botón guardar? Tenemos un evento en FileSavePickerActivatedEventArgs que nos indica exactamente esto: TargetFileRequested, en este evento podemos guardar el archivo donde deseemos:

Guardando un archivo
  1. async void FileSavePickerUI_TargetFileRequested(Windows.Storage.Pickers.Provider.FileSavePickerUI sender, Windows.Storage.Pickers.Provider.TargetFileRequestedEventArgs args)
  2. {
  3.     var deferral = args.Request.GetDeferral();
  4.  
  5.     args.Request.TargetFile = await ApplicationData.Current.LocalFolder.CreateFileAsync(sender.FileName);
  6.  
  7.     deferral.Complete();
  8. }

Ya que la llamada es asíncrona, usamos un deferral para indicar a la aplicación Host cuando hemos terminado la operación mediante el método Complete(), siendo la aplicación host la encargada de mostrar la UX de espera al usuario:

image

Conclusión

En las aplicaciones Metro los contratos nos permitirán expandir nuestras aplicaciones mucho más allá de su interface de usuario, con lo que podremos crear nuevas formas de interactuar con el usuario y sus datos. En próximos capítulo veremos nuevos contratos como el de compartir o el de búsqueda y que implicaciones tenemos que tener en cuenta a la hora de usarlos. Como siempre, aquí tenéis el código de ejemplo del artículo de hoy, espero que haya sido de interés y ayuda.

Un saludo y Happy Coding!

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

Hola a todos!

En los artículos anteriores de la serie, hemos visto los fundamentos básicos para usar MVVM en una aplicación Metro: ViewModel Base, ViewModel Locator y Navegación. Hoy vamos a ver las particularidades del ciclo de vida de una aplicación Metro y lo parecido que este ciclo es al que tenemos en Windows Phone 7.5.

Ciclos de vida: Windows 8 vs Windows Phone 7.5

image

Como podemos observar en la comparación anterior, ambos sistemas comparten la misma base en su ciclo de vida: Una aplicación entra en ejecución, se suspende por una acción del usuario (cambio a otra aplicación, terminación, un fallo…) y una vez suspendida puede volver a entrar en ejecución por que el usuario la recupere o quedar indefinidamente en este estado hasta que el sistema decida terminarla por necesidades de rendimiento o cualquier otra consideración.

Sin embargo, si vemos los diagramas, Windows 8 parece ofrecer un ciclo de vida más simple (lo que no significa que sea menos potente o nos de menos opciones). Además, como añadido en Windows 8 la activación de nuestra aplicación puede venir dada por varios caminos, como veremos a continuación.

Estado: Activación

En Windows Phone nuestra aplicación podía ser Activada generalmente por dos vías: El usuario la lanzaba desde el tile de aplicación o volvíamos a ejecución después de estar en suspensión o en tombstoning.

En Windows 8, si el usuario lanza nuestra aplicación desde el tile de la misma o desde un tile de contenido (secundario), recibiremos el evento OnLaunched, situado en nuestro archivo app.xaml.cs:

Application OnLaunched
  1. protected override void OnLaunched(LaunchActivatedEventArgs args)
  2. {
  3.     // Create a Frame to act navigation context and navigate to the first page
  4.     var rootFrame = new Frame();
  5.     rootFrame.Navigate(typeof(BlankPage));
  6.  
  7.     // Place the frame in the current Window and ensure that it is active
  8.     Window.Current.Content = rootFrame;
  9.     Window.Current.Activate();
  10. }

Dentro de este evento se crea el Frame principal de nuestra aplicación, navegamos a nuestra página inicial y activamos la ventana principal. Además en el parámetro LaunchActivatedEventArgs podremos comprobar cual fue el estado anterior de ejecución de nuestra aplicación, de esta forma podemos comportarnos de diferente forma dependiendo de si la aplicación fue suspendida, terminada, cerrada por el usuario… todo esto a partir del enumerador ApplicationExecutionState:

Enum ApplicationExecutionState
  1. switch (args.PreviousExecutionState)
  2. {
  3.     case ApplicationExecutionState.ClosedByUser:
  4.     {
  5.  
  6.         break;
  7.     }
  8.     case ApplicationExecutionState.NotRunning:
  9.     {
  10.         break;
  11.     }
  12.     case ApplicationExecutionState.Running:
  13.     {
  14.         break;
  15.     }
  16.     case ApplicationExecutionState.Suspended:
  17.     {
  18.         break;
  19.     }
  20.     case ApplicationExecutionState.Terminated:
  21.     {
  22.         break;
  23.     }
  24. }

Pero Windows 8 ofrece nuevas formas de lanzar nuestra aplicación. Podemos responder a otros tipos de activación. Por poner un ejemplo, podemos indicar que el usuario puede buscar datos en nuestra aplicación, gestionar archivos y algunas otras opciones. Para estos casos especiales disponemos de Métodos de activación independientes que nos permitirán ejecutar la lógica necesaria:

Método Responde a…
OnSearchActivated Se lanza cuando la aplicación es iniciada desde la búsqueda del sistema operativo.
OnFileActivated Cuando se abre un archivo para cuyo tipo nos hemos registrado como que podemos abrirlo.
OnFileSavePickerActivated Si otra aplicación quiere guardar un archivo que podemos manejar, el usuario podrá escoger nuestra aplicación como destino y recibiremos este evento
OnFileOpenPickerActivated Al igual que al guardar, también podemos indicar que pueden abrir archivos de nuestra aplicación y nos llamarán a través de este evento.
OnShareTargetActivated Si nuestra aplicación es capaz de compartir información a redes sociales, el usuario puede usarla desde otras aplicaciones y recibiremos este evento

En el código del App.xaml.cs podremos manejar el evento o eventos que deseemos:

Eventos de activaci&amp;amp;amp;#243;n
  1. protected override void OnSearchActivated(SearchActivatedEventArgs args)
  2. {
  3.     base.OnSearchActivated(args);
  4. }
  5.  
  6. protected override void OnFileActivated(FileActivatedEventArgs args)
  7. {
  8.     base.OnFileActivated(args);
  9. }
  10.  
  11. protected override void OnFileSavePickerActivated(FileSavePickerActivatedEventArgs args)
  12. {
  13.     base.OnFileSavePickerActivated(args);
  14. }
  15.  
  16. protected override void OnFileOpenPickerActivated(FileOpenPickerActivatedEventArgs args)
  17. {
  18.     base.OnFileOpenPickerActivated(args);
  19. }
  20.  
  21. protected override void OnShareTargetActivated(ShareTargetActivatedEventArgs args)
  22. {
  23.     base.OnShareTargetActivated(args);
  24. }

En próximos artículos veremos como activar cada una de estas actividades y como responder a ellas. Ahora lo importante es saber que estos son posibles puntos de entrada a nuestra aplicación.

Forzar Suspensión y Reinicio

Antes de pasar a ver como trabajar con los eventos de Suspensión y Reinicio, es importante que sepamos como forzar su ejecución. En Visual Studio 11 Beta debemos mostrar la barra de Debug Location (View > Toolbars > Debug Location) donde tendremos botones específicos para Suspender y reiniciar nuestra aplicación:

image

Estado: Suspensión

De forma muy parecida a Windows Phone, el estado de suspensión de una aplicación en Windows 8 se establece siempre que la aplicación no está viéndose en pantalla. Cuando esto ocurra, podemos tener una oportunidad de preservar información manejando el evento Suspending de la clase App:

Evento Suspending
  1. public App()
  2. {
  3.     this.InitializeComponent();
  4.     App.Current.Suspending += OnSuspending;
  5. }

Una vez que recibamos la llamada al método que maneja el evento Suspending, dispondremos de un máximo de 5 segundos (tiempo de procesador, no real) para guardar nuestros datos de forma segura:

Manejador OnSuspending
  1. void OnSuspending(object sender, SuspendingEventArgs e)
  2. {
  3.     KeyValuePair<string, object> data = new KeyValuePair<string,object>("Nombre", "yeray");
  4.     if (!ApplicationData.Current.LocalSettings.Values.Contains(data))
  5.         ApplicationData.Current.LocalSettings.Values.Add(data);
  6. }

Dentro del parámetro SuspendingEventArgs podemos encontrar una propiedad llamada SuspendingOperation que contiene toda la información sobre la suspensión que se está llevando a cabo. Una propiedad interesante es la Deadline que nos indica la fecha / hora exacta en la que termina el tiempo que tenemos para guardar los datos.

Dentro de esta propiedad SuspendingOperation tenemos un método GetDeferral que nos permite obtener un objeto del tipo SuspendingDeferral, una vez obtenido podemos ejecutar nuestro código de guardado y llamar al método Completed de SuspendingDeferral para indicar al sistema que hemos terminado y puede continuar con la suspensión. Esto no evita que debamos cumplir con el Deadline indicado en SuspendingOperation:

Uso de GetDeferral()
  1. void OnSuspending(object sender, SuspendingEventArgs e)
  2. {
  3.     SuspendingDeferral def = e.SuspendingOperation.GetDeferral();
  4.  
  5.     KeyValuePair<string, object> data = new KeyValuePair<string,object>("Nombre", "yeray");
  6.     if (!ApplicationData.Current.LocalSettings.Values.Contains(data))
  7.         ApplicationData.Current.LocalSettings.Values.Add(data);
  8.  
  9.     def.Complete();
  10. }

 

NOTA: Además de todo esto hay algo más que debemos tener en cuenta: Windows 8 administrará su memoria para tener el mayor número de aplicaciones en suspensión posible, pero si necesita liberar memoria empezará por las aplicaciones que más gasten, sin tener en cuenta la antigüedad de las aplicaciones en suspensión, por esto es buena idea que intentemos guardar en el almacenamiento todo lo que podamos e intentar que nuestra aplicación en suspensión sea lo más liviana posible, para tener más oportunidades de permanecer en suspensión.

Visibilidad

Aunque no se trata de un estado de la aplicación, sí es una parte importante de su ciclo de vida. En Windows Phone una vez que el usuario sale de nuestra aplicación por cualquier motivo (sin cerrarla) recibimos de manera automática el evento Deactivated que correspondería al evento Suspending de Windows 8. Pero en Windows 8 esta operación puede tener un retraso de hasta 10 segundos, tiempo durante el cual nuestra aplicación sigue en ejecución aunque no esté visible. Si volvemos a nuestra aplicación durante este tiempo, no se llevará a cabo la suspensión y reinicio de la aplicación, en su lugar solo se cambiará la propiedad Visibility de la ventana principal.

Podemos controlar estos cambios de visibilidad manejando el evento VisibilityChanged de nuestra ventana principal, justo después de activarla (como podemos ver en la línea tres del siguiente código):

Manjeando VisibilityChanged
  1. Window.Current.Content = rootFrame;
  2. Window.Current.Activate();
  3. Window.Current.VisibilityChanged += Current_VisibilityChanged;

Una vez recibido el evento podemos realizar acciones dependiendo de si estamos ocultándonos o volviendo a ser visibles:

Manejador VisibilityChanged
  1. void Current_VisibilityChanged(object sender, Windows.UI.Core.VisibilityChangedEventArgs e)
  2. {
  3.     if (e.Visible)
  4.         Text = "Application visible";
  5.     else
  6.         Text = "Application hidden";
  7. }

Estado: Reinicio

Una vez que nuestra aplicación esté suspendida pueden ocurrir dos cosas: que el usuario vuelva a activarla en algún momento o que, por necesidades del sistema, Windows 8 la termine y elimine completamente de memoria. En el primer caso, recibiremos el evento Resuming al que podemos subscribirnos para realizar acciones adicionales necesarias para volver a poner en funcionamiento nuestra aplicación:

Registro de eventos
  1. public App()
  2. {
  3.     this.InitializeComponent();
  4.     App.Current.Suspending += OnSuspending;
  5.     App.Current.Resuming += OnResuming;
  6. }

Una vez subscritos al evento, podremos cargar valores que hayamos guardado en el ApplicationData durante el evento Suspending:

Manejador OnResuming
  1. void OnResuming(object sender, object e)
  2. {
  3.     Text = ApplicationData.Current.LocalSettings.Values["Nombre"].ToString();
  4. }

LocalSettings vs RoamingSettings

Antes de continuar con el último de los estados posibles, quiero dedicarle un poco de atención a unas propiedades nuevas y extremadamente interesantes de la clase ApplicationData en Windows 8: LocalSettings y RoamingSettings. Estas dos propiedades tienen el mismo funcionamiento exacto:

RoamingSettings
  1. ApplicationData.Current.RoamingSettings.Values.Add(data);
  2. Text = ApplicationData.Current.RoamingSettings.Values["Nombre"].ToString();

¿Cual es su diferencia? Pues básicamente LocalSettings guarda la información en el dispositivo que estemos usando, mientras que RoamingSettings la almacena en la nube y su contenido será accesible por nuestra aplicación en otros dispositivos que usen el mismo LiveID que estamos usando en este. Con esta pequeña diferencia, podremos realizar aplicaciones que se sincronicen entre varios dispositivos.

Podemos subscribirnos al Evento DataChanged de la clase ApplicationData (línea 7) para que se nos notifique cuando los datos en roaming han cambiado y así obtener los nuevos valores:

Evento DataChanged
  1. public App()
  2. {
  3.     this.InitializeComponent();
  4.     App.Current.Suspending += OnSuspending;
  5.     App.Current.Resuming += OnResuming;
  6.  
  7.     ApplicationData.Current.DataChanged += Current_DataChanged;
  8. }
  9.  
  10. void Current_DataChanged(ApplicationData sender, object args)
  11. {
  12.     Text = sender.RoamingSettings.Values["Nombre"].ToString();
  13. }

Ejemplo

Para terminar este artículo he creado un ejemplo que va añadiendo los distintos estados por los que transita a una colección y mostrando esta colección en una lista en pantalla para que podamos comprobar como afectan nuestras acciones al ciclo de vida de la aplicación:

image

 

Conclusión

Con este artículo hemos visto todo lo que el ciclo de vida de una aplicación Metro tiene que ofrecer y como se parece y se diferencia de lo aprendido en Windows Phone, aunque son muy parecidos, se aprecia el trabajo de Microsoft en darnos un ciclo de vida más potente y la capacidad de realizar Roaming de los datos de nuestra aplicación podrá marcar la diferencia… Ahora solo necesitamos que Windows Phone lo soporte también, para poder realizar aplicaciones en ambas plataformas que se sincronicen entre sí, teniendo una versión tablet  y una versión móvil que nos permitan interactuar sobre la misma información de maneras distintas. Como siempre os dejo el código del ejemplo aquí para que podáis jugar con el, nos vemos en el próximo artículo de la serie.

Un saludo y Happy Coding!

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

Hola a todos!

En el primer artículo de esta serie pudimos ver como la implementación del patrón MVVM no había sufrido demasiadas variaciones y también como el apartado de Navegación había variado notablemente. Hoy quiero dedicar este segundo artículo de la serie a los mecanismos a usar para conectar nuestras Views con sus respectivas ViewModels.

Enlazando Views y ViewModels

Tras ver como podíamos usar el patrón MVVM perfectamente en una aplicación Windows 8 Metro, quiero explorar las opciones que tenemos para “casar” nuestra View con su ViewModel correspondiente, de la forma más limpia y organizada que sea posible. Para empezar, vamos a ver como lo haríamos en Windows Phone 7.5:

En Windows Phone 7.5 el enlace entre View y ViewModel lo haríamos a través de 3 pasos fundamentales. En primer lugar podríamos usar IoC para tener un mecanismo que resuelva nuestras ViewModels a partir de su Interface. Tras esto, podríamos usar una clase Locator que se encargaría de exponer propiedades, una por cada ViewModel, para que finalmente podamos asignar mediante enlaces de datos estas propiedades al DataContext de nuestra View. El proceso seguiría el siguiente esquema:

image

La ventaja de esta forma de trabajar es que obtenemos un único punto donde se resuelve la ViewModel a usar para cada View, además, al trabajar centrándonos en Interfaces para resolverlas podemos cambiar la implementación de forma sencilla y sin afectar a la View.

En una aplicación Metro, los pasos son casi idénticos, la mayor diferencia con Windows Phone es que, debido a que la plataforma es muy nueva, no existen todavía implementaciones de los IoC más usados como puede ser Unity o NInject por lo que esta parte, al menos por ahora deberemos resolverlo de otra forma. Salvo esto, el resto de pasos son iguales.

Para nuestro ejemplo crearemos un nuevo proyecto de C# + XAML en blanco (Windows Metro Style > Blank Application). Vamos a tener una ViewModel muy sencilla que herede de una clase base donde se implemente el interface INotifyPropertyChanged (como ya vimos en el artículo anterior) y que simplemente exponga una propiedad HelloMessage:

ViewModel VMStartPage
  1. public class VMStartPage : VMBase
  2. {
  3.     public VMStartPage()
  4.     {
  5.         HelloMessage = "Hi! Windows 8";
  6.  
  7.         RaiseChange("HelloMessage");
  8.     }
  9.  
  10.     public String HelloMessage { get; set; }
  11. }

Como podemos ver es muy sencilla, también tendremos una página inicial que mostrará la cadena de texto de HelloMessage en el centro de la pantalla:

Pagina BlankPage.xaml
  1. <Page
  2.     x:Class="Win8MetroMVVM.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:Win8MetroMVVM"
  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 ApplicationPageBackgroundBrush}">
  11.         <TextBlock Text="{Binding HelloMessage}" FontSize="122" Foreground="White"
  12.                    VerticalAlignment="Center" HorizontalAlignment="Center">
  13.         </TextBlock>
  14.     </Grid>
  15. </Page>
 

Como se puede apreciar, hemos indicado en la propiedad Text del textblock que use la propiedad HelloMessage de nuestra ViewModel, pero no le hemos dicho cual es la ViewModel que deseamos usar, eso a continuación!

Implementar ViewModelLocator

Una vez que tenemos nuestra View y su ViewModel creadas, vamos a crear un Locator, esta clase será la encargada de exponer la instancia de nuestra ViewModel cada vez que la View la necesite. Realizaremos esto usando por un lado una variable privada del tipo Lazy<T> y una propiedad pública del tipo de nuestra ViewModel, de esta forma la primera vez que se pida la ViewModel se creará y en consecuentes peticiones ya tendremos creada nuestra instancia:

Clase ViewModelLocator
  1. public class ViewModelLocator
  2. {
  3.     Lazy<VMStartPage> startPage;
  4.  
  5.     public ViewModelLocator()
  6.     {
  7.         startPage = new Lazy<VMStartPage>(() => { return new VMStartPage(); });
  8.     }
  9.  
  10.     /// <summary>
  11.     /// Returns the active instance of startPage viewmodel
  12.     /// </summary>
  13.     public VMStartPage StartPage
  14.     {
  15.         get
  16.         {
  17.             return startPage.Value;
  18.         }
  19.     }
  20. }

Como podemos ver es una clase muy sencilla. Ahora solo tenemos que acabar de atar todos los cabos, vamos a por el enlace a datos!

Realizar los Bindings

En primer lugar vamos a compilar nuestra aplicación (sin ejecutar) y abrimos el archivo App.xaml, donde vamos a añadir una referencia al namespace de nuestro ViewModelLocator (línea 5) y un nuevo recurso que sea el propio ViewModelLocator (línea 11):

App.xaml
  1. <Application
  2.     x:Class="Win8MetroMVVM.App"
  3.     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  4.     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  5.     xmlns:vmLocator="using:Win8MetroMVVM.ViewModels"
  6.     xmlns:local="using:Win8MetroMVVM">
  7.  
  8.     <Application.Resources>
  9.         <ResourceDictionary>
  10.             
  11.             <vmLocator:ViewModelLocator x:Key="Locator"></vmLocator:ViewModelLocator>
  12.             
  13.             <ResourceDictionary.MergedDictionaries>
  14.                 <ResourceDictionary Source="Common/StandardStyles.xaml"/>
  15.             </ResourceDictionary.MergedDictionaries>
  16.  
  17.         </ResourceDictionary>
  18.     </Application.Resources>
  19. </Application>

Ahora podemos ir a nuestra página BlankPage.xaml y añadir a la definición de la página la propiedad DataContext, enlazada a nuestro nuevo recurso Locator:

Cabecera BlankPage.xaml
  1. <Page
  2.     x:Class="Win8MetroMVVM.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:Win8MetroMVVM"
  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.     DataContext="{Binding StartPage, Source={StaticResource Locator}}">

Si hemos seguido todos los pasos correctamente, podremos ejecutar la aplicación y veremos un resultado que debería parecerse a este:

image

BONUS: Lo mejor de todo es que hemos realizado todo esto y tenemos nuestra ViewModel funcionando sin escribir ni una sola línea de Code Behind!!

Conclusión

Con lo visto en estos dos primeros artículos de la serie, ya podemos empezar a plantear nuestras aplicaciones Metro, usando MVVM, resolviendo nuestras ViewModel con una clase Locator y realizando enlace a datos. Como de costumbre, aquí os dejo el proyecto de ejemplo para que podáis jugar con el hasta el próximo artículo!

Un saludo y Happy Coding!

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

Hola a todos!

Hoy vamos a cambiar un poco la temática del blog, para hablar de Windows 8 y más concretamente de las aplicaciones Metro. Vamos a dar un vistazo a las similitudes y diferencias que encontraremos en este tipo de aplicaciones desde el punto de vista de un desarrollador Windows Phone.

Una de las cosas que nos sorprenderá cuando empecemos a trabajar con Windows 8 y sus aplicaciones Metro, será el gran número de similitudes que encontraremos con la plataforma Windows Phone. Aunque sutiles, también encontraremos diferencias en el desarrollo, pero son fáciles de comprender y acostumbrarnos a ellas es más fácil todavía. Vamos a comenzar por los cimientos.

Unos buenos cimientos: MVVM

Una de las cosas que no ha cambiado en Windows 8 es la forma de aplicar el patrón MVVM en nuestra aplicación. Podemos coger la estructura de una aplicación Windows Phone y trasladarla fácilmente a Windows 8. Las expresiones de enlace a datos siguen funcionando como siempre lo han  hecho y podemos establecer nuestra ViewModel como DataContext de una página.

En Windows Phone es muy normal usar una viewmodel base, que implemente INotifyPropertyChanged y para las colecciones apoyarnos en ObservableCollection<T>.

En la versión developer preview de Win8 esto nos podía dar algún dolor de cabeza (mira este artículo de Eduard Tomas) pues existían dos implementaciones de INotifyPropertyChanged: la de siempre, que se encuentra en System.ComponentModel y la nueva implementación en WinRT que se encontraba en Windows.UI.Xaml.Data Esto hacía que los cambios no se notificasen correctamente. Pero parece que en la nueva versión Consumer preview se han unificado de nuevo, manteniéndose en System.ComponentModel, y desapareciendo de Windows.UI.Xaml.Data.

De esta forma, podemos crear una ViewModel base realmente sencilla y totalmente intercambiable entre Windows Phone y Windows 8:

VMBase
  1. using System.ComponentModel;
  2.  
  3. namespace Geeks.ms.ViewModels.Base
  4. {
  5.     public class VMBase : INotifyPropertyChanged
  6.     {
  7.         public VMBase()
  8.         {
  9.         }
  10.  
  11.         public event PropertyChangedEventHandler PropertyChanged;
  12.  
  13.         public void RaiseChange(string name)
  14.         {
  15.             if (PropertyChanged != null)
  16.                 PropertyChanged(this, new PropertyChangedEventArgs(name));
  17.         }
  18.     }
  19. }

Navegación

El mayor cambio que encontraremos al implementar MVVM se centrará fundamentalmente en la forma de navegar entre las páginas de nuestra aplicación. En Windows Phone, una clase normal de navegación que pudiese ser usada desde cualquier ViewModel simplemente tenía que obtener el contener principal de la aplicación, expuesto a través de la propiedad RootVisual del objeto App:

Navegación en Windows Phone
  1. public void NavigateTo(String navigationTarget)
  2. {
  3.     PhoneApplicationFrame rootFrame = Application.Current.RootVisual as PhoneApplicationFrame;
  4.     rootFrame.Navigate(registeredViews[navigationTarget]);
  5. }

En Windows 8 no disponemos de la propiedad RootVisual. Al inicializar la aplicación se crea un objeto Frame que es el encargado de gestionar la navegación. Lo que necesitamos para poder crear un servicio de navegación básico es crear una propiedad estática en nuestra clase App que nos devuelva este Frame principal:

RootFrame
  1. sealed partial class App : Application
  2. {
  3.     /// <summary>
  4.     /// Application main frame, used for navigation
  5.     /// </summary>
  6.     public static Frame RootFrame = new Frame();

En el evento OnLaunched usaremos este RootFrame para navegar a nuestra página inicial y lo estableceremos como el contenido de nuestra ventana:

Evento OnLaunched
  1. protected override void OnLaunched(LaunchActivatedEventArgs args)
  2. {
  3.     if (args.PreviousExecutionState == ApplicationExecutionState.Terminated)
  4.     {
  5.     }
  6.  
  7.     // Create a Frame to act navigation context and navigate to the first page
  8.  
  9.     RootFrame.Navigate(typeof(BlankPage));
  10.  
  11.     // Place the frame in the current Window and ensure that it is active
  12.     Window.Current.Content = RootFrame;
  13.     Window.Current.Activate();
  14. }

Ahora solo nos queda obtener una referencia a este Frame principal en nuestra clase de navegación y usarlo en nuestros métodos:

Clase NavigationService
  1. using System;
  2. using Geeks.ms.Views;
  3. using Windows.UI.Xaml.Controls;
  4.  
  5. namespace Geeks.ms.Services.Navigation
  6. {
  7.     public class NavigationService : INavigationService
  8.     {
  9.         private readonly Frame frame;
  10.  
  11.         public NavigationService()
  12.         {
  13.             frame = App.RootFrame;
  14.         }
  15.  
  16.         public void NavigateBack()
  17.         {
  18.             frame.GoBack();
  19.         }
  20.  
  21.         public bool CanNavigateBack()
  22.         {
  23.             return frame.CanGoBack;
  24.         }
  25.  
  26.         public void NavigateToArticle(Uri articleUri)
  27.         {
  28.             frame.Navigate(typeof(VArticle), articleUri);
  29.         }
  30.     }
  31. }

Y ya podremos invocar a esta clase desde nuestras ViewModel. Otro punto de la navegación que cambia con respecto a Windows Phone es la cache de las páginas visitadas. En Windows Phone si llamábamos al método GoBack del NavigationService, automáticamente volvíamos a la página anterior, que se conservaba en el estado en el que la habíamos dejado.

En Windows 8 esto cambia, por defecto las instancias de las páginas no se conservan, la pila de navegación solo conoce el tipo y cuando hacemos un GoBack (o un GoForward, que sí está soportado en Windows 8 y nos ahorrará problemas de navegación circular) simplemente se crea una nueva instancia de la página anterior, perdiendo el estado que tenía. Este comportamiento puede ser modificado mediante la propiedad NavigationCacheMode presente en la clase Page:

Cabecera de una página Xaml
  1. <Page
  2.     x:Class="Geeks.ms.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:Geeks.ms"
  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.     NavigationCacheMode="Enabled"
  10.     DataContext="{Binding Path=StartPage, Source={StaticResource VmLocator}}">

La propiedad NavigationCacheMode puede tener tres valores:

  1. Enabled: La instancia se conserva en memoria, siempre y cuando no superemos el número de páginas máximo que hayamos establecido en nuestro Frame principal.
  2. Required: La instancia de esta página no cuenta en el total de páginas indicado en el Frame principal, simplemente debe ser conservada y no será destruida aunque se supere el máximo.
  3. Disabled: Este es el valor por defecto, se crea una nueva instancia por cada visita.

Por defecto, el objeto Frame establece la propiedad CacheSize con un tamaño de 10 páginas, aunque podemos modificarlo simplemente estableciendo la propiedad CacheSize de nuestro RootFrame:

CacheSize
  1. RootFrame.CacheSize = 12;

Algo que debemos tener en cuenta es que, si marcamos la propiedad NavigationCacheMode como Enabled o Required, si al navegar a una página se encuentra su instancia en memoria, se usará esta instancia. Si tenemos una página cuyo contenido cambiará cada vez que accedamos a ella, lo mejor es no habilitar el cache, pues no lo necesitamos.

Algo sobre lo que todavía estoy investigando y realizando pruebas es todo lo relacionado al IoC, en cuanto haya revisado todas las opciones que hay, os contaré mis ideas finales!.

Conclusión

Esto ha sido una breve introducción a las aplicaciones Metro en C#, desde el punto de vista de un desarrollador Windows Phone. En el próximo artículo veremos más detalles sobre Expression Blend 5, un poco de Async, el emulador de Windows 8 y un ejemplo de aplicación metro, así que empezad a descargar Windows 8 y Visual Studio 11 si no lo tenéis ya, es realmente divertido y vale la pena ponerle las manos encima cuanto antes.

Un saludo y Happy Coding!

[OFF TOPIC] Nokia Developer Champion 2012

nok_champion

Hola a todos!

Me ha hecho mucha ilusión ver en mi bandeja de entrada este correo:

“Since 2006, Forum Nokia has been selecting individuals from the Nokia developer community to join the Nokia Developer Champion program; a recognition and reward program that honors a select group of mobile developers from around the world. We are pleased to inform you that as one of the outstanding talents in our developer community,you have been selected as one of a few elite members of the Nokia Developer Champion Program. Congratulations!”

Y quería compartirlo con todos vosotros.

Muchas gracias a todos por el apoyo que me dais y por leer mi blog, preguntar y escuchar mis respuestas. Esto es gracias a vosotros!

Un saludo y Happy Coding!

Publicado por Josué Yeray Julián Ferreiro con 3 comment(s)
Archivado en: ,

[Windows Phone 7.5] Promociones y Concursos

Hola a todos!

   

Hoy para empezar el fin de semana con buen pie quiero comentaros algunos concursos y promociones sobre desarrollo para Windows Phone que se han puesto en marcha recientemente por parte de Microsoft y Nokia. En todos ellos tenemos la posibilidad de conseguir móviles Lumia 800 de Nokia para poder realizar  nuestras aplicaciones así que no se puede dejar escapar!

Microsoft a vuelto a arrancar el concurso semanal de desarrollo para Windows Phone que habían llevado a cabo en Navidades. El premio: 5 Lumia 800 a la semana, ¿Cómo conseguirlo? Desarrollas una aplicación y la subes al marketplace, luego vas a la página del concurso (aquí) e inscribes tu aplicación. Entre todos los participantes semanales se sortearán 3 Nokia Lumia 800. Pero la gran novedad de esta edición es que, antes de realizar el sorteo, un jurado compuesto por gente de Microsoft escogerá semanalmente 2 aplicaciones por su calidad, originalidad, UX, uso de capacidades, etc... y estos dos se llevarán directamente un Lumia 800 y no participarán en el sorteo. Moraleja: Dale toda la calidad que puedas a tu aplicación, trabájala y tendrás muchas posibilidades de tener un Lumia 800 en tus manos!

El concurso se llevará acabo desde el 16 de abril hasta el 10 de junio, así que ya puedes correr a empezar a desarrollar. Cuando tengas tu aplicación en el marketplace registrate en el concurso para optar al sorteo de esa misma semana y mucha suerte :)

Cuenta de publicador

Yeray, todo esto está muy bien pero... Tengo que hacerme una cuenta de publicador y cuesta 75 € y soy estudiante/estoy en paro/no quiero pagar... ¿Qué puedo hacer? Pues no hay problema. Microsoft puede ayudaros con un cupón de subscripción gratuita de 1 año al marketplace de Windows Phone. Para esto tenéis que escribir a la gente de soporte de Windows Phone en Microsoft Ibérica: sopwp7@microsoft.com les contáis que queréis desarrollar apps para Windows Phone y que os gustaría tener un cupón de registro. Pedirán que les habléis de las aplicaciones, que mandéis alguna captura de la App ejecutándose en el emulador y sin poneros ningún problema y muy rápidamente os enviarán el cupón y ya está! solucionado, un año gratis en el marketplace para que podáis realizar el próximo Angry Birds, eso sí, si os hacéis ricos, el segundo año ya lo tenéis que pagar!

Esto es todo amigos!

Espero que todo esto ayude a decidiros a apostar por Windows Phone y el desarrollo. Ya me contaréis que apps han salido de estas promociones.

Un saludo y Happy Coding!

[Windows Phone 7.5] Creando un DataContextProxy

Hola a todos!

Hoy vamos a ver un tema muy interesante y que puede salvarnos en alguna ocasión. Hablamos del DataContextProxy pero… ¿Qué es esto Yeray? Vamos allá!

¿Qué es un DataContextProxy y para que sirve?

Cuando creamos una aplicación Windows Phone usando el patrón MVVM (esto es, siempre. Porque siempre usamos MVVM ¿Verdad?) Establecemos nuestra ViewModel en la propiedad DataContext de nuestra View, de forma que todos nuestros controles lo usan como fuente de datos y nos permite enlazarnos a las propiedades de la VievModel, algo así:

image

Un caso muy simple, un ComboBox que mediante enlace a datos y apoyándose en el DataContext de la View accede a una propiedad expuesta por el ViewModel. No existe ninguna complejidad en este escenario.

Ahora, vamos a imaginar un caso más complejo: Tenemos el ComboBox incluido como parte de un DataTemplate de un ListBox:

image

Con esta configuración, el DataContext del ComboBox corresponde a cada Item del ListBox, es decir, una instancia de la clase Customer, por lo que no podemos acceder a las propiedades expuestas por la ViewModel. Esta situación es muy común, por ejemplo si queremos tener un botón como parte de un DataTemplate y que ese botón use un ICommand expuesto en nuestra ViewModel. Normalmente, podríamos acceder al DataContext de la página, usando la propiedad del Binding ElementName:

<ListBox Name="lstCustomers" ItemsSource="{Binding Customers}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel>
                <TextBlock Text="{Binding CustomerName}"></TextBlock>
                <Button Content="Show" Command="{Binding DataContext.ShowCustomerCommand, ElementName=lstCustomers}"></Button>
            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

Esto es totalmente correcto y nos permitiría acceder a la propiedad DataContext del elemento lstCustomers, con lo que ya tendríamos acceso a la ViewModel, pero no es algo que me parezca demasiado elegante de usar. Como las expresiones de enlace a datos no se validan en tiempo de diseño, a no ser que enlacemos con un StaticResource donde se valida su nombre, si cambiamos el nombre de la lista, aunque sea en una mayúscula… no lo encontrará y pasaremos un buen rato buscando el porqué no funciona nuestro comando.

Es aquí donde entra en juego el DataContextProxy. Básicamente se trata de un objeto que hereda de FrameworkElement (la clase base que incorpora a los controles, entre otras, la funcionalidad DataContext) y que es capaz de exponer mediante una propiedad el DataContext de su objeto padre, aprovechándonos de la herencia de propiedades que tiene FrameworkElement:

image

 

Siempre y cuando no establezcamos directamente la propiedad DataContext de un elemento, se heredará el valor de su contenedor, es por esto que podemos establecer la propiedad DataContext solo en la View y no tenemos que ir estableciéndola en cada control de la página.

Nuestro DataContextProxy se aprovechará de esta característica para exponer a través de una propiedad el DataContext de nuestra página. Podremos incluirlo en la misma como un recurso y acceder a el mediante la expresión StaticResource, lo que nos dará validación del nombre fuente en tiempo de diseño.

Muy bien, ¿Como lo implementamos?

Lo mejor de todo es que crear y usar un DataContextProxy es realmente trivial y sencillo. Lo primero que necesitamos es crear una nueva clase en nuestro proyecto Windows Phone que herede de FrameworkElement:

public class DataContextProxyService : FrameworkElement
{
    public DataContextProxyService()
    {
        this.Loaded += new RoutedEventHandler(DataContextProxyService_Loaded);
    }

    void DataContextProxyService_Loaded(object sender, RoutedEventArgs e)
    {
    }
}

Lo primero que haremos en el constructor de nuestra clase es manejar el evento Loaded para poder ejecutar el código necesario cuando nuestro servicio haya sido inicializado por la página que lo contenga.

A continuación vamos a crear una DependencyProperty que será el contenedor a través del cual expondremos nuestro DataContext:

public static readonly DependencyProperty DataSourceProperty =
    DependencyProperty.Register("DataSource", typeof(Object), typeof(DataContextProxyService), null);

public Object DataSource
{
    get { return (Object)GetValue(DataSourceProperty); }
    set { SetValue(DataSourceProperty, value); }
}

Ahora solo nos queda un último paso: Cuando recibamos el evento Loaded, vamos a crear un Binding que exponga nuestro DataContext a través de la propiedad DataSource:

void DataContextProxyService_Loaded(object sender, RoutedEventArgs e)
{
    Binding binding = new Binding();
    binding.Source = this.DataContext;
    this.SetBinding(DataContextProxyService.DataSourceProperty, binding);
}

Y con esto ya tenemos terminada nuestra clase DataContextProxy, ahora vamos a probarla, para empezar vamos a crear una página que tenga un Listbox con un DataTemplate que muestre un TextBlock con el nombre de un Item y un Button:

<ListBox ItemsSource="{Binding Customers}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel>
                <TextBlock Text="{Binding Name}"></TextBlock>
                <Button Content="Click Me!"
                        Command="{Binding SayhelloCommand}"></Button>
            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

Vamos a enlazar el DataContext de nuestra página a una ViewModel que contenga una Lista de Customers y un comando que se ejecute al presionar el botón:

public class VMMainPage : INotifyPropertyChanged
{
    private List<Customer> customers;
    public List<Customer> Customers 
    {
        get
        {
            return customers;
        }
        set 
        {
            customers = value;
            RaisePropertyChanged("Customers");
        }
    }

    private ICommand sayHelloCommand = null;
    public ICommand SayHelloCommand
    { 
        get
        {
            if (sayHelloCommand == null)
                sayHelloCommand = new DelegateCommand(() => { MessageBox.Show("Hello!"); });
            return sayHelloCommand;
        }
    }

    public VMMainPage()
    {
        var newCustomers = new List<Customer>()
                                        {
                                            new Customer() { Name = "Yeray" },
                                            new Customer() { Name = "Vicenç"},
                                            new Customer() { Name = "Ibon"}
                                        };

        Customers = newCustomers;
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void RaisePropertyChanged(string propertyName)
    { 
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

Si ejecutamos la aplicación veremos el siguiente resultado, el nombre del customer se muestra correctamente pero el comando no funciona:

image

Si miramos la ventana de Output veremos los siguientes errores:

image

Efectivamente, el sistema de enlace de Silverlight está buscando el comando SayHelloCommand dentro de cada item de la lista, por supuesto no lo encuentra. Aquí es donde entra en acción nuestro DataContextProxy: Lo primero es añadir una referencia a nuestro ensamblado en la página para que podamos acceder a los objetos de nuestra aplicación:

xmlns:dataproxy="clr-namespace:WP75DataContextProxy"

A continuación, vamos a definir una instancia de nuestra clase DataContextProxyService como recurso de la página en la que nos encontramos:

<phone:PhoneApplicationPage.Resources>
    <dataproxy:DataContextProxyService x:Key="DataProxy"></dataproxy:DataContextProxyService>
</phone:PhoneApplicationPage.Resources>

Y por último vamos a modificar el Binding del comando en el botón para que haga uso de la propiedad DataSource de nuestro DataContextProxyService:

<Button Content="Click Me!"
        Command="{Binding DataSource.SayHelloCommand, Source={StaticResource DataProxy}}">
</Button>

Esta vez, si iniciamos la aplicación y presionamos alguno de los botones, el resultado será distinto, el sistema de enlace a datos encontrará el comando en nuestro DataContext principal, el asignado a la ventana y no lo buscará en cada Item:

image

 

Conclusión

Usando esta técnica tenemos una forma sencilla y clara de poder obtener nuestro DataContext principal dentro de listas, combos, plantillas y otros elementos. Todo esto sin tener que referenciar elementos por nombre, usando un recurso estático y sin depender de elementos que mañana pueden no ser necesarios y puedan ser eliminados de la aplicación. Como siempre aquí os dejo el código de ejemplo que he usado en el artículo.

Espero que os sea muy útil. Happy Coding!

[Windows Phone 7.5] SDK 7.1.1 RTW con licencia GO LIVE

wplogo

 

Hola a todos!

Un post rápido, ya está disponible para descarga la versión final del SDK 7.1.1 de Windows Phone. Entra las novedades de esta versión final tenemos:

  • Imagen de emulador de 512Mb usa la build del sistema 8773
  • Actualización del SDK de Advertising incluida con solución a varios bugs
  • Soporte en intellisenses para especificar el requerimiento de 512Mb en el manifiesto
  • Soporte completo en Windows 8

Además de las novedades en cuanto a los dispositivos de baja memoria, sorprende la rapidez que han tenido en hacer que el emulador funcione en Windows 8, lo cual seguro que contenta a más de uno (yo me incluyo).

Podéis descargaros el SDK 7.1.1 aquí.

Disfrutadlo y Happy Coding!

Publicado por Josué Yeray Julián Ferreiro con no comments
Archivado en: ,,

[EVENTO CODEMOTION] Windows Phone 7.5 Design Patterns & Application Quality

Hola a todos!

wplogoimage

Ayer 24 de Marzo tuve la oportunidad de asistir a la primera edición en España del CODEMOTION. Además de poder asistir a algunas interesantes charlas y poder desvirtualizar a Luis Fraile y a El bruno entre otros, también tuve la oportunidad de impartir una charla sobre desarrollo avanzado para Windows Phone 7.5. En esta ocasión pudimos hablar sobre el patrón MVVM, Inyección de dependencias, servicios y testeo unitario.

Algo antes de comenzar la charla tuve la sorpresa de saber que Oscar Gutierrez, developer relations manager de Nokia Spain, vendría a acompañarme un rato en la misma para hacer una interesante proposición a los asistentes y presentar los recursos de desarrollo que Nokia tiene disponibles para todos los desarrolladores de Windows Phone.

En esta ocasión quise destacar lo importante de desarrollar bien desde el principio, usando el patrón correcto, pensando en realizar código desacoplado, basado en servicios y usando IoC para resolver dependencias. También vimos como poder testear nuestro código de una forma eficiente e integrada con Visual Studio.

A continuación os dejo la presentación de la charla, en Slideshare, desde donde también podréis descargarla:

El proyecto que usé para ilustrar todos estos principios fue el que he desarrollado para nuestro podcast WPControla, podéis ver el código en http://wpcontrola.codeplex.com

Espero que os guste, yo desde luego disfruté como un niño en la charla.

Un abrazo y Happy Coding!

[Windows Phone 7.5] Podcast en español WPControla, episodio 3

wpcontrola_tile
Hola a todos!

Dicen que no hay dos sin tres así qué, aquí estamos con el tercer capítulo del podcast sobre desarrollo para Windows Phone que hago con Rafael Serna.

En este tercer capítulo entrevistamos a Jorge Cantón de Syderis, uno de los creadores del SDK multiplataforma CellSDK. También desgranamos las últimas noticias y hablamos sobre la publicidad en las aplicaciones y como mejorar un poco nuestras ganancias con ella.

Tienes un resumen completo y los enlaces del podcast aquí.

Espero que disfrutéis tanto escuchándolo como yo haciéndolo.

Un saludo y Happy Coding!

[Windows Phone 7.5] Creando nuestros propios Behaviors

Hola a todos!

En el último artículo sobre Expression Blend (puedes verlo aquí) hablamos sobre animaciones y como usar los behaviors incluidos en Silverlight para ejecutarlas. Pudimos ver como existen diferentes tipos de behaviors, pero no vimos como poder crear los nuestros propios para poder usarlos en nuestras aplicaciones.

Behavior<T>

Dentro del ensamblado System.Windows.Interactivity tenemos una clase llamada Behavior<T> de la cual podemos heredar para crear un behavior personalizado. La estructura mínima es la siguiente:

public class CustomBehavior : Behavior<Grid>
{
    protected override void OnAttached()
    {
        base.OnAttached();
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();
    }
}

Con el código anterior indicamos que queremos crear un Behavior llamado CustomBehavior que podrá ser usado en elementos de tipo Grid. El método OnAttached se ejecutará cuando el behavior se añada al elemento Grid y el OnDetaching cuando se descargue del elemento. De esta forma podremos ejecutar el código que deseemos y tendremos una forma de limpiar las referencias, eventos, y todo lo necesario cuando el Behavior ya no sea necesario.

Un ejemplo práctico: Extendiendo el control WebBrowser

El control WebBrowser incluido en el SDK de Windows Phone es muy versátil para mostrar contenido HTML, incluso contiene un método llamado NavigateToString que nos permite mostrar en el control una cadena de texto con formato HTML. El problema que tiene es que no existe una propiedad que podamos usar desde XAML directamente para mostrar el contenido de una propiedad, siempre necesitamos usar Code Behind para atacar a este método, algo parecido a esto:

<Grid x:Name="LayoutRoot" Background="Transparent">       
    <phone:WebBrowser Name="webControl"></phone:WebBrowser>
</Grid>
public MainPage()
{
    InitializeComponent();

    webControl.NavigateToString("<BODY>Hola!</BODY>");
}

Esto es muy engorroso si estamos usando un patrón MVVM, además de romper la regla de no usar Code Behind, tenemos que detectar los cambios en nuestro ViewModel y de alguna forma invocar código en nuestro code behind o tener una referencia al control directamente en nuestra ViewModel, lo cual es aun peor.

¿Como lo resolvemos? Usando un Behavior!

Lo primero que vamos a hacer es crear una nueva clase llamada NavigateToStringBehavior que herede de Behavior, pasando como tipo el control WebBrowser. Dentro de nuestro behavior tenemos una propiedad llamada AssociatedObject, que nos devuelve el objeto al cual hemos asociado nuestro behavior, con todas las propiedades del tipo indicado:

public class NavigateToStringBehavior : Behavior<Microsoft.Phone.Controls.WebBrowser>
{
    /// <summary>
    /// Execute when attached to a parent object.
    /// </summary>
    protected override void OnAttached()
    {
        base.OnAttached();
        AssociatedObject.Loaded += AssociatedObject_Loaded;
    }

    /// <summary>
    /// Execute our behavior actions!
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    void AssociatedObject_Loaded(object sender, RoutedEventArgs e)
    {
    }

    /// <summary>
    /// Execute when detached from a parent object
    /// </summary>
    protected override void OnDetaching()
    {
        base.OnDetaching();
        AssociatedObject.Loaded -= AssociatedObject_Loaded;
    }
}

En este caso usamos el método OnAttached para manejar el evento Loaded de nuestro WebBrowser, mediante la propiedad AssociatedObject y el método OnDetaching para eliminar el manejador creado de forma que no queden dependencias y se pueda liberar correctamente toda la memoria al destruir el WebBrowser.

Ahora necesitamos algún mecanismo que nos permita indicarle a nuestro behavior cual es el contenido que deseamos mostrar, además queremos que este contenido pueda ser indicado mediante un Binding a una propiedad de nuestra ViewModel, la solución: Añadir una DependencyProperty a nuestro behavior:

private static readonly DependencyProperty HtmlContentProperty = DependencyProperty.Register("HtmlContent", typeof(string),
                                                                                                typeof(NavigateToStringBehavior), 
                                                                                                null);
public string HtmlContent 
{
    get { return (string)this.GetValue(HtmlContentProperty); }
    set { this.SetValue(HtmlContentProperty, value); }
}

Para ello creamos una propiedad pública en nuestra clase NavigateToStringBehavior de tipo string llamada HtmlContent y a continuación una propiedad de tipo DependencyProperty que será la encargada de almacenar nuestro valor.

Ahora solo nos queda añadir código al manejador de eventos AssociatedObject_Loaded para que obtenga el valor de la propiedad HtmlContentProperty y llame al método NavigateToString del WebBrowser asociado:

/// <summary>
/// Execute our behavior actions!
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void AssociatedObject_Loaded(object sender, RoutedEventArgs e)
{
    AssociatedObject.NavigateToString(HtmlContent);
}

El código simplemente llama al método NavigateToString pasando como argumento la propiedad HtmlContent, que a su vez obtendrá el valor de la DependencyProperty HtmlContentProperty evaluando la expresión de enlace a datos (binding) si existe.

Usando el fruto de nuestro trabajo

Ahora vamos a crear una clase llamada HtmlViewModel en nuestra aplicación que imitará a una ViewModel real y expondrá dos propiedades de tipo string:

public class HtmlViewModel
{
    public string Html1 { get; set; }

    public string Html2 { get; set; }

    public HtmlViewModel()
    {
        Html1 = @"<BODY bgcolor='#AAAAFF' text='#003300'>
                    <strong>Hola!</strong>
                  </BODY>";

        Html2 = @"<BODY bgcolor='#FFAAAA' text='#003300'>
                    <strong>Adios!</strong>
                  </BODY>";
    }
}

En estas dos propiedades hemos introducido código HTML para imitar el posible HTML que podríamos tener guardado de un Rss o una página web.

Vamos a abrir Expression Blend (Haciendo click derecho en nuestra página MainPage.xaml y escogiendo la opción “Open in Expression Blend”).

He creado un layout con dos WebBrowsers en la página:

image

 

Lo primero que necesitamos hacer es indicarle a nuestra página que debe usar la clase HtmlViewModel como su DataContext. Para ello seleccionamos en la pestaña de objetos nuestra PhoneApplicationPage y en las propiedades buscamos el DataContext, donde encontraremos el botón New, al presionarlo abrirá una ventana para seleccionar nuestra fuente de datos:

image

Después de seleccionar la  clase HtmlViewModel presionamos OK. Veremos una lista de las propiedades que expone nuestra clase, Html1 y Html2, ahora seleccionamos el primer WebBrowser y vamos a la pestaña Assets y a los Behaviors, donde veremos que está incluido nuestro NavigateToStringBehavior:

image

Solo tenemos que seleccionarlo y arrastrarlo sobre nuestro primer WebBrowser. Si intentamos añadirlo a la grid o a la página, veremos que nos indica un mensaje de que no es un objetivo válido. una vez añadido al primer WebBrowser, en las propiedades del Behavior veremos nuestra DependencyProperty HtmlContent, donde podremos indicar nuestro Binding:

image

Podemos repetir la misma operación con el segundo WebBrowser, añadiendo un nuevo NavigateToStringBehavior y estableciendo el Binding a la propiedad Html2.

Tras hacer esto, si ejecutamos nuestra aplicación veremos el resultado:

image

Y Voila! tenemos nuestro código HTML mostrándose en un WebBrowser sin necesidad de usar Code Behind y con un Behavior reutilizable siempre que lo necesitemos y con soporte a enlace a datos.

Conclusión

Hemos podido ver como crear un Behavior es muy sencillo pero al mismo tiempo muy útil y potente, es una herramienta que nos permitirá crear comportamientos reutilizables, usables desde Expression Blend directamente y evitando usar Code Behind. Como siempre, aquí tenéis el proyecto de ejemplo, espero que os sea de utilidad.

Un saludo y Happy Coding!

[Windows Phone 7.5] Materiales de mi Webcast sobre Expression Blend 4

Ayer jueves 15 de Marzo MSDN Latam me volvió a ofrecer la oportunidad de realizar un Webcast sobre desarrollo con Expression Blend 4 para Windows Phone 7.5

Podéis ver el evento grabado en el siguiente enlace: https://www137.livemeeting.com/cc/MSEvents/view?cn=guest&id=1032507359&pw=E5720F15

He colgado la presentación en SlideShare para que sea más comodo verla:

Espero que hayáis disfrutado tanto con el webcast como yo haciéndolo. Un saludo y Happy Coding!

Publicado por Josué Yeray Julián Ferreiro con no comments
Archivado en: ,,

[Windows Phone Tango] Preparando nuestras aplicaciones (I)

Hola a todos!

Como ya vimos anteriormente, Microsoft ha liberado la CTP del SDK de Windows Phone Tango. Se estima que los dispositivos ejecutando esta versión de Windows Phone aumentarán el mercado disponible en un 60% por lo que es más que recomendable empezar a preparar nuestras aplicaciones para esta nueva versión del sistema operativo.

Una de las limitaciones más evidentes será el recorte en la memoria disponible para nuestra aplicación que pasará de 90Mb a 60Mb por lo que, si no lo habéis hecho ya, es buen momento para empezar a usar el profiler de Windows Phone y ver donde podemos optimizar nuestro consumo de memoria.

Introducción a la herramienta de análisis de rendimiento de Windows Phone

Una de las novedades que tuvimos con el SDK de Windows Phone 7.1 fue la inclusión de un profiler específico para nuestras aplicaciones móviles, el “Windows Phone Performance Analysis” que podemos encontrar en el menú Debug de Visual Studio 2010.

Al iniciarlo se nos presentarán dos opciones distintas:

image

En esta primera pantalla podremos decidir entre analizar el rendimiento visual y de código o analizar el consumo de memoria. También podemos ver como se nos indica en forma de Warning que estamos usando el emulador y no un dispositivo real, con lo que el rendimiento puede ser distinto al de un dispositivo real (y de echo lo es y mucho) y que para obtener unos resultados más precisos debemos usar la configuración de release. En este primer ejemplo vamos a seleccionar “Memory” y dejar los valores por defecto.

Por último tenemos un link para iniciar la aplicación, lo que dejará el profiler a la escucha de los resultados e iniciará la compilación y despliegue:

image

En este momento deberemos usar nuestra aplicación de forma normal. Podemos centrarnos en una página en concreto e ir midiendo el rendimiento que tiene bajo diferentes situaciones o hacer un barrido por toda la aplicación para ver una primera aproximación del consumo de memoria. En cualquier caso, cuando hayamos terminado, presionamos Stop Profiling y Visual Studio empezará a procesar los datos y nos los mostrará en una línea de tiempo:

image

En esta primera ejecución estamos usando una aplicación vacía, tal cual la crea el SDK, por lo que no vemos ninguna variación en el consumo de memoria y solo se produce un evento de recolección, al terminar la misma. Podemos seleccionar una porción de esta línea para ver un detalle de lo que ha ocurrido en ese momento exacto, simplemente pinchando en un punto inicial y arrastrando hasta un punto final:

image

Si presionamos “Start Analysis” se nos mostrará en un primer momento los avisos de rendimiento:

image

Si presionamos sobre la flecha a continuación de “Performance Warnings” podremos seleccionar “Heap Summary” donde podremos ver un sumario de uso de memoria, nuevas inclusiones… :

image

Si seleccionamos la línea “Retained Silverlight Visuals at End” podremos acceder a los tipos que contiene:

image

Por cada tipo además de ver su tamaño total, máximo durante el periodo seleccionado y el número de instancias, podremos ver una descripción de cada instancia:

image

Con esto, vemos el momento de creación, cuanto tiempo se mantuvo viva, quien lo creo y el tamaño en Bytes que ocupa.

Con esta información, podremos ver de forma rápida donde estamos aumentando el consumo de memoria, si esta se libera cuando debe o no, quien está creando objetos que no debería, etc… . Usarla de forma correcta nos ayudará a tener una aplicación más eficiente y poder llegar al nuevo 60% de los dispositivos que usarán Windows Phone Tango.

En el próximo artículo profundizaremos en la optimización del uso de memoria en nuestras aplicaciones, uso de imágenes, hilos y más cosas interesantes y curiosas.

Un saludo y Happy Coding!

Más artículos Página siguiente >