jueves, 12 de agosto de 2010

WP7 & Silverlight: Un toque 3D en nuestras aplicaciones Móviles y Web

Hola a todos!
Una de las grandes ventajas de las que presume Windows Phone 7 (WP7) es el poder portar de manera muy sencilla nuestro código Silverlight a los teléfonos móviles y, en realidad, Microsoft tiene todas las razones para presumir, portar código desde Silverlight 3/4 a Silverlight for Mobile (la versión de silverlight que usa WP7) es realmente sencillo, en muchos casos no es más complicado que copiar y pegar.
Desde hace tiempo tenía ganas de hacer un artículo sobre las capacidades de proyección 3D de objetos que incorpora Silverlight desde la versión 3 y que, por supuesto está incluido en Silverlight for mobile. Como tampoco quiero dejar de escribir e investigar sobre WP7, pensé que sería una buena idea hacer un post combinado de las dos plataformas, pues, como veréis, con solo copiar y pegar podemos pasar nuestro código de una a otra.

NOTA: WPF 4 tiene capacidades 3D mucho más potentes que Silverlight en cualquiera de sus ediciones, pero, como dicen: “un gran poder, conlleva una gran responsabilidad”, es un poco más complicado de realizar y el código no es transportable a Silverlight o WP7, por ello, dejo el toque 3D en WPF para el próximo artículo. Aun así, empezar por silverlight nos ayudará a definir muchos conceptos y aprenderemos nuevas artes para nuestras futuras aplicaciones en WP7.

Un poco de teoría.

La clase PlaneProjection

Silverlight, tanto su versión Web como su versión para WP7, no tiene soporte 3D real como su hermano mayor WPF, por esto desde la versión 3 tenemos a nuestra disposición la clase PlaneProjection, que nos permite realizar transformaciones de perspectiva en un objeto cualquiera:
rotation
En la imagen anterior, hemos aplicado la clase PlaneProjection a un objeto Grid, con lo que la perspectiva se aplica también a los controles que contiene, que siguen siendo totalmente operativos.
<Grid.Projection>
<PlaneProjection x:Name="GridProjection" RotationY="-90">
</PlaneProjection>
</Grid.Projection>


La clase PlaneProjection contiene 12 propiedades que nos permitirán realizar todo tipo de transformaciones en perspectiva:


CenterOfRotationX


Esta propiedad establece donde se sitúa el eje X durante la rotación del objeto. Por defecto el valor asignado es 0.5, esto es, el medio de la altura del objeto. Un valor de 0 indica que el eje X se sitúa en el borde superior del objeto y un Valor 1 indica que se sitúa en el borde inferior.


CenterOfRotationY


Esta propiedad establece donde se sitúa el eje Y durante la rotación del objeto. Por defecto el valor asignado es 0.5, esto es, el medio de la anchura del objeto. Un valor de 0 indica que el eje Y se sitúa en el borde izquierdo del objeto y un Valor 1 indica que se sitúa en el borde derecho.


CenterOfRotationZ


Esta propiedad establece donde se sitúa el eje Z durante la rotación del objeto. Por defecto el valor asignado es 0, esto es, el mismo plano del objeto. Los valores menores que 0 indican que el eje Z se sitúa tras el objeto y los valores superiores a 0 que se sitúa sobre el objeto.


RotationX


El valor de esta propiedad indica los grados que el objeto sobre el que estamos aplicando la proyección va a girar sobre su eje X, esto es, alrededor de una línea imaginaria que lo cruza de izquierda a derecha y que por defecto se encuentra situada a media altura del objeto:


rotationX


RotationY


El valor de esta propiedad indica los grados que el objeto sobre el que estamos aplicando la proyección va a girar sobre su eje Y, esto es, alrededor de una línea imaginaria que lo cruza de arriba a abajo y que por defecto se encuentra situada a media anchura del objeto:


rotationY


RotationZ


El valor de esta propiedad indica los grados que el objeto sobre el que estamos aplicando la proyección va a girar sobre su eje Z, esto es, alrededor de una línea imaginaria que lo cruza de alante hacia atras y que por defecto se encuentra en el centro del objeto:


RotationZ






 


GlobalOffsetX


El valor de esta propiedad indica el desplazamiento que le aplicaremos a nuestro objeto en el eje X de la pantalla. Un valor de 0 indica que el objeto no se desplaza, un valor negativo hará que el objeto se desplace hacia la izquierda y un valor positivo hará que el objeto se desplace a la derecha. Este desplazamiento no tiene en cuenta la rotación del objeto, se realiza solo teniendo en cuenta el eje X de la pantalla.


GlobalOffsetY


El valor de esta propiedad indica el desplazamiento que le aplicaremos a nuestro objeto en el eje Y de la pantalla. Un valor de 0 indica que el objeto no se desplaza, un valor negativo hará que el objeto se desplace hacia arriba y un valor positivo hará que el objeto se desplace hacia abajo. Este desplazamiento no tiene en cuenta la rotación del objeto, se realiza solo teniendo en cuenta el eje Y de la pantalla.


GlobalOffsetZ


El valor de esta propiedad indica el desplazamiento que le aplicaremos a nuestro objeto en el eje Z de la pantalla. Un valor de 0 indica que el objeto no se desplaza, un valor negativo hará que el objeto se desplace hacia el fondo y un valor positivo hará que el objeto se desplace hacia la pantalla. Este desplazamiento no tiene en cuenta la rotación del objeto, se realiza solo teniendo en cuenta el eje Z de la pantalla.


LocalOffsetX


Esta propiedad indica el desplazamiento que le aplicaremos a nuestro objeto sobre su eje X. Un valor de 0 indica que el objeto no se desplaza, un valor negativo lo desplazará a la izquierda y un valor positivo lo desplazará a la derecha. Este desplazamiento usa el eje X del objeto, por lo que el desplazamiento obtenido dependerá de la rotación del objeto.


LocalOffsetY


Esta propiedad indica el desplazamiento que le aplicaremos a nuestro objeto sobre su eje Y. Un valor de 0 indica que el objeto no se desplaza, un valor negativo lo desplazará arriba y un valor positivo lo desplazará abajo. Este desplazamiento usa el eje Y del objeto, por lo que el desplazamiento obtenido dependerá de la rotación del objeto.


LocalOffsetZ


Esta propiedad indica el desplazamiento que le aplicaremos a nuestro objeto sobre su eje Z. Un valor de 0 indica que el objeto no se desplaza, un valor negativo lo desplazará atrás y un valor positivo lo desplazará adelante. Este desplazamiento usa el eje Z del objeto, por lo que el desplazamiento obtenido dependerá de la rotación del objeto.


La clase Storyboard



En Silverlight (y en WPF) podemos realizar animaciones de forma muy sencilla mediante el uso de la clase Storyboard. Esta clase actúa como contenedor de distintas clases de animación. Hay dos tipos de animaciones, animaciones de valores desde hasta y animaciones basadas en cuadros. (para una descripción a fondo de cada tipo podéis mirar aquí y aquí)  con estos dos tipos de animaciones podremos animar casi cualquier tipo de propiedad de nuestros objetos.



<Storyboard x:Name="Grid360Y">
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" 
Storyboard.TargetName="GridProjection" 
Storyboard.TargetProperty="RotationY">
<LinearDoubleKeyFrame KeyTime="00:00:00" Value="0">
</LinearDoubleKeyFrame>
<LinearDoubleKeyFrame KeyTime="00:00:02" Value="360">
</LinearDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
</Storyboard>



Aquí podéis ver un ejemplo sencillo de animación usando KeyFrames. Nuestro objeto Storyboard es el contenedor para nuestros objetos de animación, en este caso usamos la clase DoubleAnimationUsingKeyFrames, que indica que la propiedad que queremos animar es de tipo Double, de esta forma encontrareis que para la mayoría de tipos de datos existe un TIPODATOAnimation o un TIPODATOAnimationUsingKeyFrames, para que uséis el que más se ajuste a vuestras necesidades. Dentro del objeto DoubleAnimationUsingKeyFrames definimos 3 propiedades:


BeginTime


Indica el momento tras iniciar el Storyboard en que esta animación se ejecutará, esto es muy útil, pues podemos tener varias animaciones distintas en un mismo Storyboard y nos permite de esta forma “orquestar” toda la animación para que cada una se ejecute en el momento apropiado.


Storyboard.TargetName


Esta propiedad heredada de Storyboard especifica el nombre del objeto sobre el que se va a ejecutar la animación.


Storyboard.TargetProperty


El nombre de la propiedad del objeto indicado sobre la que vamos a ejecutar la animación.


Dentro del objeto DoubleAnimationUsingKeyFrames tenemos dos objetos LinearDoubleKeyFrame que serán los encargados de realizar la transición del valor de nuestra propiedad al valor indicado por su propiedad Value, las propiedades que usamos son:


KeyTime


Indica el tiempo, relativo al BeginTime de la DoubleAnimationUsingKeyFrames, en que la propiedad indicada en Storyboard.TargerProperty tendrá el nuevo valor indicado en Value.


Value


Indica el nuevo valor de la propiedad indicada en Storyboard.TargetProperty.


Además de LinearKeyFrame también existe la clase SplineKeyFrame que nos permite usar una curva Spline para dotar de mayor realismo a nuestra animación, produciendo aceleraciones y desaceleraciones, podéis verla en detalle aquí.


Con esto hemos terminado con la teoría, lo mejor de todo es que todo esto que hemos visto funciona desde la versión 3 de Silverlight y, gracias a que Silverlight para WP7 es un superset de Silverlight 3 con algunos añadidos de Silverlight 4, también funciona de forma natural en Windows Phone 7 así que vamos a ver como usarlo.


A por la práctica



Silverlight 4



Vamos a empezar creando un nuevo proyecto de Silverlight 4 en Visual Studio 2010.


Dentro de la grid LayoutRoot que se crea por defecto creamos un StackPanel que contenga todos nuestros botones:


<StackPanel>
<Button Name="btnRotateX" Content="Rotate X 360º" 
Click="btnRotateX_Click" Margin="5" Height="35">
</Button>
<Button Name="btnRotateY" Content="Rotate Y 360º" 
Click="btnRotateY_Click" Margin="5" Height="35">
</Button>
<Button Name="btnTranslateLocalX" Content="Translate X" 
Click="btnTranslateLocalX_Click" Margin="5" Height="35">
</Button>
<Button Name="btnTranslateLocalY" Content="Translate Y" 
Click="btnTranslateLocalY_Click" Margin="5" Height="35">
</Button>
<Button Name="btnRotateXTranslate" Content="Rotate X + Translate Z" 
Click="btnRotateXTranslate_Click" Margin="5" Height="35">
</Button>
<Button Name="btnRotateYTranslate" Content="Rotate Y + Translate X" 
Click="btnRotateYTranslate_Click" Margin="5" Height="35">
</Button>
<Button Name="btnReset" Content="Reset" 
Click="btnReset_Click" Margin="5" Height="35">
</Button>
</StackPanel>


Una vez hecho esto vamos a definir el PlaneProjection de nuestra grid LayoutRoot, justo después del cierre del stackpanel, dentro de la grid:


<Grid.Projection>
<PlaneProjection x:Name="GridProjection">
</PlaneProjection>
</Grid.Projection>


Ahora debemos definir nuestras animaciones, las introduciremos como recursos de nuestra página:


<UserControl.Resources>
<Storyboard x:Name="Grid360Y">
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" 
Storyboard.TargetName="GridProjection" 
Storyboard.TargetProperty="RotationY">
<LinearDoubleKeyFrame KeyTime="00:00:00" Value="0"></LinearDoubleKeyFrame>
<LinearDoubleKeyFrame KeyTime="00:00:02" Value="360"></LinearDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Name="Grid360X">
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" 
Storyboard.TargetName="GridProjection" 
Storyboard.TargetProperty="RotationX">
<LinearDoubleKeyFrame KeyTime="00:00:00" Value="0"></LinearDoubleKeyFrame>
<LinearDoubleKeyFrame KeyTime="00:00:02" Value="360"></LinearDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Name="GridTranslateLocalX">
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" 
Storyboard.TargetName="GridProjection" 
Storyboard.TargetProperty="LocalOffsetX">
<LinearDoubleKeyFrame KeyTime="00:00:00" Value="0"></LinearDoubleKeyFrame>
<LinearDoubleKeyFrame KeyTime="00:00:01" Value="30"></LinearDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Name="GridTranslateLocalY">
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" 
Storyboard.TargetName="GridProjection" 
Storyboard.TargetProperty="LocalOffsetY">
<LinearDoubleKeyFrame KeyTime="00:00:00" Value="0"></LinearDoubleKeyFrame>
<LinearDoubleKeyFrame KeyTime="00:00:01" Value="30"></LinearDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
</Storyboard>

<Storyboard x:Name="RotateYTranslateX">
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" 
Storyboard.TargetName="GridProjection" 
Storyboard.TargetProperty="RotationY">
<LinearDoubleKeyFrame KeyTime="00:00:00" Value="-90"></LinearDoubleKeyFrame>
<LinearDoubleKeyFrame KeyTime="00:00:01" Value="0"></LinearDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" 
Storyboard.TargetName="GridProjection" 
Storyboard.TargetProperty="LocalOffsetX">
<LinearDoubleKeyFrame KeyTime="00:00:00" Value="180"></LinearDoubleKeyFrame>
<LinearDoubleKeyFrame KeyTime="00:00:01" Value="0"></LinearDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" 
Storyboard.TargetName="LayoutRoot" 
Storyboard.TargetProperty="Opacity">
<LinearDoubleKeyFrame KeyTime="00:00:00" Value="0"></LinearDoubleKeyFrame>
<LinearDoubleKeyFrame KeyTime="00:00:02" Value="1"></LinearDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Name="RotateXTranslateZ">
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" 
Storyboard.TargetName="GridProjection" 
Storyboard.TargetProperty="RotationX">
<LinearDoubleKeyFrame KeyTime="00:00:00" Value="360"></LinearDoubleKeyFrame>
<LinearDoubleKeyFrame KeyTime="00:00:01" Value="0"></LinearDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" 
Storyboard.TargetName="GridProjection" 
Storyboard.TargetProperty="LocalOffsetZ">
<LinearDoubleKeyFrame KeyTime="00:00:00" Value="0"></LinearDoubleKeyFrame>
<LinearDoubleKeyFrame KeyTime="00:00:01" Value="-180"></LinearDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" 
Storyboard.TargetName="LayoutRoot" 
Storyboard.TargetProperty="Opacity">
<LinearDoubleKeyFrame KeyTime="00:00:00" Value="0"></LinearDoubleKeyFrame>
<LinearDoubleKeyFrame KeyTime="00:00:01" Value="1"></LinearDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</UserControl.Resources>


Aquí podéis ver un ejemplo de lo que explicaba anteriormente, en los dos últimos Storyboards, tenemos varios objetos DoubleAnimationUsingKeyFrames que animan distintas propiedades de nuestro objeto PlaneProjection e incluso animando una propiedad de la grid de forma directa.


Bien, una vez hecho esto solo nos queda incorporar el código a nuestra aplicación, como este ejemplo intenta comparar las similitudes entre Silverlight y Silverlight Mobile el código se encuentra en C#, pero en el proyecto que tenéis más abajo para descargar tenéis el código de Silverlight en Visual Basic y C# y el de WP7 en C# que es el lenguaje soportado por ahora.


public MainPage()
{
InitializeComponent();

RotateYTranslateX.Begin();
}

private void btnRotateX_Click(object sender, RoutedEventArgs e)
{
Grid360X.Begin();
}

private void btnRotateY_Click(object sender, RoutedEventArgs e)
{
Grid360Y.Begin();
}

private void btnTranslateLocalX_Click(object sender, RoutedEventArgs e)
{
GridTranslateLocalX.Begin();
}


private void btnRotateXTranslate_Click(object sender, RoutedEventArgs e)
{
RotateXTranslateZ.Begin();
}

private void btnRotateYTranslate_Click(object sender, RoutedEventArgs e)
{
RotateYTranslateX.Begin();
}

private void btnTranslateLocalY_Click(object sender, RoutedEventArgs e)
{
GridTranslateLocalY.Begin();
}

private void btnReset_Click(object sender, RoutedEventArgs e)
{
GridProjection.LocalOffsetX = 0;
GridProjection.LocalOffsetY = 0;
GridProjection.LocalOffsetZ = 0;
}


Como podéis ver el código es muy sencillo. La clase Storyboard puede ser accedida por código y expone varios métodos y eventos. En este código usamos el método Begin que da inicio a la animación, podemos controlar cuando se produce el final de la animación con el evento Completed y también disponemos de métodos para parar o pausar la misma.


Si ejecutamos nuestra aplicación Silverlight veremos una sencilla animación al inicio y presionando cada botón ejecutaremos las animaciones correspondientes.


No ha sido tan complicado, pero, ¿y si queremos ahora tener nuestro trabajo en WP7? Pues, amigos, eso es todavía más sencillo partiendo de esta base.


Silverlight Mobile



wp7


Sin cerrar nuestro proyecto de Silverlight, abrid otra instancia de VIsual Studio 2010 (o Visual Studio 2010 por Windows Phone) y cread un nuevo proyecto Silverlight for Windows Phone.


Ahora vamos a Abrir el Xaml del archivo MainPage y vamos a copiar desde nuestra aplicación silverlight todos los Storyboards e incluirlos en un nuevo tag de recursos del Xaml de nuestra aplicación WP7:


<phone:PhoneApplicationPage.Resources>
<Storyboard x:Name="Grid360Y">
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" 
Storyboard.TargetName="GridProjection" 
Storyboard.TargetProperty="RotationY">
<LinearDoubleKeyFrame KeyTime="00:00:00" Value="0"></LinearDoubleKeyFrame>
<LinearDoubleKeyFrame KeyTime="00:00:02" Value="360"></LinearDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Name="Grid360X">
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" 
Storyboard.TargetName="GridProjection" 
Storyboard.TargetProperty="RotationX">
<LinearDoubleKeyFrame KeyTime="00:00:00" Value="0"></LinearDoubleKeyFrame>
<LinearDoubleKeyFrame KeyTime="00:00:02" Value="360"></LinearDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Name="GridTranslateLocalX">
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" 
Storyboard.TargetName="GridProjection" 
Storyboard.TargetProperty="LocalOffsetX">
<LinearDoubleKeyFrame KeyTime="00:00:00" Value="0"></LinearDoubleKeyFrame>
<LinearDoubleKeyFrame KeyTime="00:00:01" Value="30"></LinearDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Name="GridTranslateLocalY">
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" 
Storyboard.TargetName="GridProjection" 
Storyboard.TargetProperty="LocalOffsetY">
<LinearDoubleKeyFrame KeyTime="00:00:00" Value="0"></LinearDoubleKeyFrame>
<LinearDoubleKeyFrame KeyTime="00:00:01" Value="30"></LinearDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
</Storyboard>

<Storyboard x:Name="RotateYTranslateX">
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" 
Storyboard.TargetName="GridProjection" 
Storyboard.TargetProperty="RotationY">
<LinearDoubleKeyFrame KeyTime="00:00:00" Value="-90"></LinearDoubleKeyFrame>
<LinearDoubleKeyFrame KeyTime="00:00:01" Value="0"></LinearDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" 
Storyboard.TargetName="GridProjection" 
Storyboard.TargetProperty="LocalOffsetX">
<LinearDoubleKeyFrame KeyTime="00:00:00" Value="180"></LinearDoubleKeyFrame>
<LinearDoubleKeyFrame KeyTime="00:00:01" Value="0"></LinearDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" 
Storyboard.TargetName="LayoutRoot" 
Storyboard.TargetProperty="Opacity">
<LinearDoubleKeyFrame KeyTime="00:00:00" Value="0"></LinearDoubleKeyFrame>
<LinearDoubleKeyFrame KeyTime="00:00:02" Value="1"></LinearDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Name="RotateXTranslateZ">
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" 
Storyboard.TargetName="GridProjection" 
Storyboard.TargetProperty="RotationX">
<LinearDoubleKeyFrame KeyTime="00:00:00" Value="360"></LinearDoubleKeyFrame>
<LinearDoubleKeyFrame KeyTime="00:00:01" Value="0"></LinearDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" 
Storyboard.TargetName="GridProjection" 
Storyboard.TargetProperty="LocalOffsetZ">
<LinearDoubleKeyFrame KeyTime="00:00:00" Value="0"></LinearDoubleKeyFrame>
<LinearDoubleKeyFrame KeyTime="00:00:01" Value="-180"></LinearDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" 
Storyboard.TargetName="LayoutRoot" 
Storyboard.TargetProperty="Opacity">
<LinearDoubleKeyFrame KeyTime="00:00:00" Value="0"></LinearDoubleKeyFrame>
<LinearDoubleKeyFrame KeyTime="00:00:01" Value="1"></LinearDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</phone:PhoneApplicationPage.Resources>


Como podéis ver el único cambio es que en vez de incluir los Storyboards en el Tag <UserControl.Resources /> los incluimos en <phone:PhoneApplicationPage.Resources /> aunque es lógico pues en Windows Phone usamos PhoneApplicationPage y no UserControl…


Por lo demás podéis observar que no se producen errores de ningún tipo y Silverlight Mobile acepta la sintaxis copiada de nuestra aplicación Silverlight 4 perfectamente.


Ahora hacemos lo mismo copiando el StackPanel y el Grid.Projection desde nuestra aplicación Silverlight a nuestra aplicación para WP7, donde lo colocaremos en la Grid ContentGrid:


<Grid x:Name="ContentGrid" Grid.Row="1">
<StackPanel>
<Button Name="btnRotateX" Content="Rotate X 360º" 
Click="btnRotateX_Click" Margin="5" Height="35"></Button>
<Button Name="btnRotateY" Content="Rotate Y 360º" 
Click="btnRotateY_Click" Margin="5" Height="35"></Button>
<Button Name="btnTranslateLocalX" Content="Translate X" 
Click="btnTranslateLocalX_Click" Margin="5" Height="35"></Button>
<Button Name="btnTranslateLocalY" Content="Translate Y" 
Click="btnTranslateLocalY_Click" Margin="5" Height="35"></Button>
<Button Name="btnRotateXTranslate" Content="Rotate X + Translate Z" 
Click="btnRotateXTranslate_Click" Margin="5" Height="35"></Button>
<Button Name="btnRotateYTranslate" Content="Rotate Y + Translate X" 
Click="btnRotateYTranslate_Click" Margin="5" Height="35"></Button>
<Button Name="btnReset" Content="Reset" 
Click="btnReset_Click" Margin="5" Height="35"></Button>
</StackPanel>

<Grid.Projection>
<PlaneProjection x:Name="GridProjection"></PlaneProjection>
</Grid.Projection>
</Grid>


Lo mismo esta vez, ningún tipo de error, por último abrir el archivo de código C# de nuestra aplicación WP7 y copiad los métodos de nuestra aplicación Silverlight (con el constructor incluido, pero sin copiar la clase):





public MainPage()
{
InitializeComponent();

RotateYTranslateX.Begin();
}

private void btnRotateX_Click(object sender, RoutedEventArgs e)
{
Grid360X.Begin();
}

private void btnRotateY_Click(object sender, RoutedEventArgs e)
{
Grid360Y.Begin();
}

private void btnTranslateLocalX_Click(object sender, RoutedEventArgs e)
{
GridTranslateLocalX.Begin();
}


private void btnRotateXTranslate_Click(object sender, RoutedEventArgs e)
{
RotateXTranslateZ.Begin();
}

private void btnRotateYTranslate_Click(object sender, RoutedEventArgs e)
{
RotateYTranslateX.Begin();
}

private void btnTranslateLocalY_Click(object sender, RoutedEventArgs e)
{
GridTranslateLocalY.Begin();
}

private void btnReset_Click(object sender, RoutedEventArgs e)
{
GridProjection.LocalOffsetX = 0;
GridProjection.LocalOffsetY = 0;
GridProjection.LocalOffsetZ = 0;
}


Otra vez, veréis que no se produce ningún tipo de error, si ejecutáis la aplicación que acabamos de crear en Windows Phone 7, veréis que es totalmente funcional y tiene el mismo comportamiento que su gemela de escritorio.


Y es esta, en mi opinión, la gran fortaleza de Windows Phone 7. Podemos desarrollar aplicaciones para él, que luego serán muy sencillas de traspasar a WPF o Silverlight y podremos ejecutarlas en el escritorio, y podemos coger nuestras aplicaciones Silverlight y transformarlas casi sin tocar nada de código en aplicaciones Windows Phone 7. Aunque para convertir una aplicación WPF tendríamos algo más de trabajo, no creáis que sería demasiado, en cualquier caso sería cuestión de revisar más las diferencias entre WPF y Silverlight que entre WPF y WP7.


Espero que os haya gustado tanto leer este artículo como a mi escribirlo.


Por si tenéis alguna dificultad, a parte por supuesto de poder contactar conmigo tanto dejando mensajes como en mi twitter, como en msdn o a mi email directamente, os dejo el proyecto completo para descarga, en el encontrareis el proyecto de WP7 y Silverlight 4 en C# y el proyecto de Silverlight 4 en Visual Basic.NET para los que (como yo) os sintáis algo más comodos en VB.NET.


Y recordad, con un simple comentario, podéis hacer feliz a un pequeño bloguero :)


Muchas gracias por leerme y Happy Coding!

6 comentarios:

  1. Hola me ha encantado tu publicación!!
    Estoy realizando mi proyecto fin de carrera de ingeniería técnica de telecomunicaciones, y he desarrollado una plataforma web para la gestión de clientes de una consultora. Lo he desarrollado con silverlight y servicios ria para los servicios de datos. Se lo he presentado a mi tutor y me ha dicho que es más un proyecto de informática que de teleco, así que me ha sugerido que incluya algo de comunicaciones.(perdona por enrrollarme tanto!!)
    ¿Sabes si desde silverlight se pueden mandar avisos por sms? ¿Crees que sería muy complicado trasladar mi aplicación a windows Phone 7?
    Muchas gracias por todo.
    Saludos

    ResponderEliminar
  2. Hola!

    Primero, muchas gracias por leerme, me alegra mucho que te haya gustado :)

    En cuanto a Silverlight y envio de sms, ten en cuenta que deberías tener un servicio ria en tu servidor que sería el que se encargase de conectarse a un servicio de mensajería para enviar sms, pero desde Silverlight directamente no puedes, debes hacerlo desde tu servicio Ria.

    En cuanto a pasar tu código a Windows Phone 7, es realmente sencillo, el 80% del código de silverlight es compatible entre la versión web y la versión movil, no creo que te cause demasiados problemas :)

    Un gran saludo y de nuevo, gracias por leerme!

    ResponderEliminar
  3. Muchas gracias por tu contestación, pero estoy un poco agobiada y no encuentro la manera de hacer lo que me comentas arriba. ¿Podrías ayudarme un poco más? ¿Sabes de algún servico de mensajería para enviar sms? ¿Cómo debería ser mi servicio RIA?

    Muchas gracias por todo!!
    Saludosssss

    ResponderEliminar
  4. Hola chiquiriki

    En estos momentos estoy con unos lios tremendos, te recomiendo que te pases por el foro de Silverlight de MSDN en español:
    http://social.msdn.microsoft.com/Forums/es-es/wpfes/threads
    Alli puedes plantear tu duda exacta y tanto yo como otros fantasticos compañeros te podemos ir orientando,así si yo estoy liado siempre habra más gente que te pueda ayudar,

    Un gran saludo y gracias por confiar en un servidor :)

    ResponderEliminar
  5. Tio, soy un principiante, se poco, este post me ha ayudado un montón, gracias :)

    ResponderEliminar
  6. Daniel de nada, también puedes encontrar nuevos artículos en mi blog actual: www.geeks.ms/blogs/jyeray

    Gracias por leerme :)

    ResponderEliminar