Windows Phone 8

Implementando Menús Laterales en una App para Windows Phone


Antes de empezar quiero compartir con ustedes la siguiente información:

El objetivo final de este tutorial es brindarles material que puedan implementar en sus Apps para Windows Phone.
Cabe destacar que es idea original de Dan Ardelean y solo lo expreso a mi manera, de tal forma que puedan entenderle un poco mejor.
Este artículo es una traducción al español encontrado en el portal developer.nokia, y aquí pueden encontrar la Nota Original

A mi manera….

En el artículo Dan comenta que tuvo una charla con su amigo Alessandro Scardova sobre la posibilidad de implementar menús secundarios dentro de una aplicación de Windows Phone (similar a las de la App de Facebook). Incluso si el diseño no es 100% “Modern UI”, pero este cuenta con un buen enfoque para las aplicaciones que tienen múltiples opciones que necesitan ser visitadas de forma rápida (también el enfoque se puede aplicar multiplataforma).
Así que previamente pensó en utilizar una plantilla panorama o de pivote, pero después de varias pruebas no fue posible obtener el comportamiento deseado.

Objetivos:
-Cuando se inicialice la aplicación se deberá visualizar la ventana grafica seleccionada.
-Poder deslizarse a la izquierda o derecha para abrir/cerrar los menús secundarios.
-Al abrir los menús secundarios, el ApplicationBar no debe de ser visible.
-Poder hacer uso de los botones de la parte superior izquierda y derecha para poder abrir los menús secundarios.

NOTA: Los menús laterales tienen una anchura menor de 480, de esta manera cuando se abre todavía se puede visualizar una parte de la ventana principal (incluyendo el botón superior).

La solución que se ha implementado no usa el patrón MVVM, es más una prueba de concepto sobre cómo implementar la funcionalidad. El enfoque es bastante simple, ya que se tiene la visión de que nos movemos dentro de un lienzo a través de manipulaciones y animaciones. Al principio se pensó que solo se podría usar la rejilla sin el lienzo (canvas) y animar el margen de la parrilla, pero como Windows Phone no tiene ThicknessAnimation, las animaciones para abrir y cerrar los menús no eran muy suaves, Dan comenta que también intento implementar el comportamiento swipe utilizando el evento Touch.FrameReported pero los resultados no fueron muy buenos.

Entonces, ¿cómo se puede realizar dicha implementación de los menús?
Respuesta rápida:
-Se tiene un canvas/grid que tiene un ancho de 1320 y la altura se extiende a toda la altura disponible.
-La vista se inserta en un canvas con una posición inicial Canvas.Left , se establece con un valor de -420, de esta manera vemos el menú principal.
-Las posiciones fijas dentro del canvas son: 0: Menú de la Izquierda se abrió, -420: Vista Principal y -840: Menú de la Derecha se abrió.
-Cuando presionemos los botones nosotros usaremos un recurso Storyboard con un DoubleAnimation para ajustar la posición Canvas.Left dentro del canvas a 0, -420 o -840.

<Canvas.Resources>


<Storyboard x:Name=”moveAnimation”>


<DoubleAnimation Duration=”0:0:0.2″ To=”0″ Storyboard.TargetProperty=”(Canvas.Left)” Storyboard.TargetName=”LayoutRoot” d:IsOptimized=”True” />


</Storyboard>

</Canvas.Resources>

Código para que la animación abra o cierre los menús.


void MoveViewWindow(double left)

{

_viewMoved = true;


if (left==-420)

ApplicationBar.IsVisible = true;


else

ApplicationBar.IsVisible = false;

((Storyboard)canvas.Resources[“moveAnimation”]).SkipToFill();

((DoubleAnimation)((Storyboard)canvas.Resources[“moveAnimation”]).Children[0]).To = left;

((Storyboard)canvas.Resources[“moveAnimation”]).Begin();

}

Para implementar el swipe utilizemos el ManipulationStarted, ManipulationDelta y ManipulationEnded en el contenedor del canvas.
En Delta fijamos el valor Canvas.Left directamente (sin necesidad de animaciones) entre un máximo de 0 y un mínimo de -840.

private
void canvas_ManipulationDelta(object sender, ManipulationDeltaEventArgs e)

{


if (e.DeltaManipulation.Translation.X != 0)


Canvas.SetLeft(LayoutRoot, Math.Min(Math.Max(-840, Canvas.GetLeft(LayoutRoot) + e.DeltaManipulation.Translation.X), 0));

}

Cuando nos deslicemos debemos de memorizar la posición inicial con Canvas.Left. Para esto hacemos la siguiente operación:
Si restando el Canvas.Left final y el inicial, el valor absoluto es menor que 100 ( no mucho swipe) nos recuperamos a la posición inicial, de lo contrario nos movemos a la siguiente posición.

private
void canvas_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e)

{


var left = Canvas.GetLeft(LayoutRoot);


if (_viewMoved)


return;


if (Math.Abs(initialPosition – left) < 100)

{


//Regresa a su estado original

MoveViewWindow(initialPosition);


return;

}


//Cambio de estado


if (initialPosition – left > 0)

{


//slide a la izquierda


if (initialPosition > -420)

MoveViewWindow(-420);


else

MoveViewWindow(-840);

}


else

{


//slide a la derecha


if (initialPosition< -420)

MoveViewWindow(-420);


else

MoveViewWindow(0);

}
}

_viewModel se utiliza para ver si la vista ya estaba movida por otro evento desde nuestra manipulación.

Pero veamos mejor el paso a paso.
Iniciemos con un nuevo proyecto para que quede más claro:

En la parte del XAML de nuestro archivo MainPage.xaml eliminemos todo lo que se encuentra debajo de:

shell:SystemTray.IsVisible=”True”>
hasta </phone:PhoneApplicationPage>

Para que insertemos lo siguiente:

<Canvas x:Name=”canvas” VerticalAlignment=”Stretch” HorizontalAlignment=”Stretch” Height=”800″ Background=”Black” ManipulationStarted=”canvas_ManipulationStarted” ManipulationDelta=”canvas_ManipulationDelta” ManipulationCompleted=”canvas_ManipulationCompleted”>


<Canvas.Resources>


<Storyboard x:Name=”moveAnimation”>


<DoubleAnimation Duration=”0:0:0.2″ To=”0″ Storyboard.TargetProperty=”(Canvas.Left)” Storyboard.TargetName=”LayoutRoot” d:IsOptimized=”True” />


</Storyboard>


</Canvas.Resources>

<Canvas
CacheMode=”BitmapCache” x:Name=”LayoutRoot” Width=”1320″
VerticalAlignment=”Stretch” Background=”Transparent” Canvas.Left=”-420″ Height=”768″>

<Border Width=”420″
CacheMode=”BitmapCache” Background=”#FF31363E” Grid.Column=”0″ VerticalAlignment=”Stretch” HorizontalAlignment=”Stretch” Height=”{Binding ActualHeight, ElementName=canvas}”/>

<Border Width=”420″ Margin=”900,0,0,0″ CacheMode=”BitmapCache” Background=”#FF31363E” Grid.Column=”2″ VerticalAlignment=”Stretch” HorizontalAlignment=”Stretch” Height=”{Binding ActualHeight, ElementName=canvas}”/>

<Grid x:Name=”grdCommands” Margin=”420,0,0,0″ CacheMode=”BitmapCache” Grid.Column=”1″ Background=”#FFCFD4E2″ Height=”{Binding ActualHeight, ElementName=canvas}” Width=”480″ >


<Grid.RowDefinitions>


<RowDefinition Height=”76″/>


<RowDefinition Height=”*”/>


</Grid.RowDefinitions>


<Grid Grid.Row=”0″ VerticalAlignment=”Stretch” HorizontalAlignment=”Stretch” Background=”#FF3B5998″>


<Button
Click=”OpenClose_Left” Content=”” HorizontalAlignment=”Left” VerticalAlignment=”Top” Width=”80″ Height=”80″/>


<Button
Click=”OpenClose_Right” Content=”” HorizontalAlignment=”Right” VerticalAlignment=”Top” Width=”80″ Height=”80″/>


</Grid>


<Border Grid.Row=”1″ Background=”#FFCFD4E2″/>


</Grid>


</Canvas>


</Canvas>

Creo y se han de preguntar, ¿Qué acabo de hacer/insertar?
Bueno primeramente contamos con un canvas (x:Name=”canvas”)el cual representa la sección principal o el menú de en medio de nuestro proyecto, este contiene un recurso de animación que será el que nos permita hacer los movimientos hacia los lados para mostrar los menús.
Después lo que hicimos fue pintar un canvas (x:Name=”LayoutRoot”) con las medidas necesarias para poder mostrar las 3 secciones cuando hagamos el swipe, y en la parte inferior añadimos los menús laterales.
También se añadió un Grid que contiene los botones que nos permitirán desplazarnos a los menús laterales en caso de no hacer el swipe.
Ya por ultimo un elemento Border para el color de fondo de nuestro Grid principal (x:Name=”grdCommands”).

Así debe de quedar:

Ahora pasémonos al código (MainPage.xaml.cs)

Eliminemos todo lo que esta comentado.
Y para que nuestro proyecto funcione correctamente deberemos de añadir 2 directivas using:

using System.Windows.Media.Animation;

using System.Windows.Input;

Después en el componente de inicialización agregaremos lo siguiente:

VisualStateManager.GoToState(this, “Normal”, false);

//Codigo para localizar el ApplicationBar
BuildLocalizedApplicationBar();

Después crearemos las acciones de los botones que añadimos con anterioridad en XAML.

private
void OpenClose_Left(object sender, RoutedEventArgs e)

{


var left = Canvas.GetLeft(LayoutRoot);


if (left > -100)

{

ApplicationBar.IsVisible = true;

MoveViewWindow(-420);

}


else

{
ApplicationBar.IsVisible = false;

MoveViewWindow(0);

}

}

private
void OpenClose_Right(object sender, RoutedEventArgs e)

{


var left = Canvas.GetLeft(LayoutRoot);


if (left > -520)

{
ApplicationBar.IsVisible = false;

MoveViewWindow(-840);

}


else

{

ApplicationBar.IsVisible = true;

MoveViewWindow(-420);

}
}

Después declaramos el método MoreViewWindow que instanciamos con anterioridad para que la animación abra o cierre los menús

void MoveViewWindow(double left)

{

_viewMoved = true;


if (left==-420)

ApplicationBar.IsVisible = true;


else

ApplicationBar.IsVisible = false;

((Storyboard)canvas.Resources[“moveAnimation”]).SkipToFill();

((DoubleAnimation)((Storyboard)canvas.Resources[“moveAnimation”]).Children[0]).To = left;

((Storyboard)canvas.Resources[“moveAnimation”]).Begin();

}

Ahora fijamos el valor directamente (sin necesidad de animaciones) entre un máximo de 0 y un mínimo de -840.

private
void canvas_ManipulationDelta(object sender, ManipulationDeltaEventArgs e)
{
if (e.DeltaManipulation.Translation.X != 0)
Canvas.SetLeft(LayoutRoot, Math.Min(Math.Max(-840, Canvas.GetLeft(LayoutRoot) + e.DeltaManipulation.Translation.X), 0));
}

El código a continuación expresa lo antes mencionado sobre la memoria inicial del canvas.

double initialPosition;


bool _viewMoved = false;


private
void canvas_ManipulationStarted(object sender, ManipulationStartedEventArgs e)

{

_viewMoved = false;

initialPosition = Canvas.GetLeft(LayoutRoot);
}

private
void canvas_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e)

{


var left = Canvas.GetLeft(LayoutRoot);


if (_viewMoved)


return;


if (Math.Abs(initialPosition – left) < 100)

{


//Regresa a la posicion original

MoveViewWindow(initialPosition);


return;

}


//Cambio de estado


if (initialPosition – left > 0)

{


//Slide a la izquierda


if (initialPosition > -420)

MoveViewWindow(-420);


else

MoveViewWindow(-840);

}


else

{


//slide a la derecha


if (initialPosition < -420)

MoveViewWindow(-420);


else

MoveViewWindow(0);

}

}

Ya por ultimo agregaremos un método que nos permitirá dibujar y localizar nuestro ApplicationBar

private
void BuildLocalizedApplicationBar()

{


//Establece el ApplicationBar en la página y lo instancia.
ApplicationBar = new
ApplicationBar();

//Crea un nuevo botón y establecer el valor de texto a la cadena localizada de AppResources.


ApplicationBarIconButton appBarButton = new
ApplicationBarIconButton(new
Uri(“/Assets/AppBar/Plus.png”, UriKind.Relative));

appBarButton.Text = AppResources.AppBarButtonText;

ApplicationBar.Buttons.Add(appBarButton);


//Crea un nuevo elemento de menú con la cadena localizada de AppResources.

ApplicationBarMenuItem appBarMenuItem = new
ApplicationBarMenuItem(AppResources.AppBarMenuItemText);

ApplicationBar.MenuItems.Add(appBarMenuItem);
}

Guardemos y compilemos para comprobar que no tengamos ningún error y después ejecutemos nuestra aplicación.


Visualización de nuestro ejemplo:

Y como siempre, aquí les comparto el código de este tutorial.
Espero y les haya servido.

Happy Coding!

P.D Agradezco a Dan Ardelean por su excelente material.

Anuncios

Responder

Por favor, inicia sesión con uno de estos métodos para publicar tu comentario:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s