Flipbook en Silverlight 3

Voy a crear una aplicación sencilla que puede ser creada mediante WPF, pero en este caso voy a crearla a través de Silverlight, coincidiendo con la puesta en marcha de la versión 3 de dicha plataforma.

La aplicación es un libro virtual, formado por una serie de fotos elegidas previamente. Al presionar sobre la primera foto esta se movería imitando a un libro. Se repetirá dicho efecto cada vez que presionemos sobre las correspondientes fotos.

Lo primero que debemos de incluir es la parte del interface de usuario, lo realizaremos en Xaml introduciendo el siguiente código:

<UserControl x:Class=»libro.MainPage»

xmlns=»http://schemas.microsoft.com/winfx/2006/xaml/presentation»

xmlns:x=»http://schemas.microsoft.com/winfx/2006/xaml»

xmlns:d=»http://schemas.microsoft.com/expression/blend/2008″ xmlns:mc=»http://schemas.openxmlformats.org/markup-compatibility/2006″

mc:Ignorable=»d» d:DesignWidth=»640″ d:DesignHeight=»480″>

<UserControl.Resources>

<!–estilo que se va aplicar a cada imagen–>

<Style x:Key=»_imageStyle»

TargetType=»Image»>

<Setter Property=»Margin»

Value=»275,0,0,0″ />

<Setter Property=»Width»

Value=»275″ />

<Setter Property=»Stretch»

Value=»Uniform» />

</Style>

</UserControl.Resources>

<Grid x:Name=»LayoutRoot»>

<Image Style=»{StaticResource _imageStyle}»

Source=»Images/1.jpg»

MouseLeftButtonUp=»FlipImage» />

<!–cada una de las imagenes lanza un evento

al pulsar sobre ellas conel botón

izquierdo del ratón–>

<Image Style=»{StaticResource _imageStyle}»

Source=»Images/2.jpg»

MouseLeftButtonUp=»FlipImage» />

<Image Style=»{StaticResource _imageStyle}»

Source=»Images/3jpg»

MouseLeftButtonUp=»FlipImage» />

<Image Style=»{StaticResource _imageStyle}»

Source=»Images/4.jpg»

MouseLeftButtonUp=»FlipImage» />

<Image Style=»{StaticResource _imageStyle}»

Source=»Images/7.jpg»

MouseLeftButtonUp=»FlipImage» />

<TextBlock Text=»Haz Clic para cambiar de imagen»

VerticalAlignment=»Bottom»

HorizontalAlignment=»Center» />

</Grid>

 

 

</UserControl>

 

Como podemos ver hemos usado un estilo que se definirá en cada una de las imágenes, en WPF este estilo se puede heredar pero en Silverlight aún no tenemos esta particularidad.

El evento que se desencadena lo tenemos que realizar a través del código Behind introduciendo el siguiente código:

public partial class MainPage : UserControl

{

private int _zIndex = 10;

public MainPage()

{

InitializeComponent();

}

private void FlipImage(object sender, MouseButtonEventArgs e)

{

Image image = sender as Image;

 

// Asegura que la imagen estará encima de todas las demás

image.SetValue(Canvas.ZIndexProperty, _zIndex++);

 

// Crea el Storyboard(guión)

Storyboard flip = new Storyboard();

 

// crea una animación de una duración

//de un segundo

DoubleAnimation animation = new DoubleAnimation()

{

Duration = new TimeSpan(0, 0, 1)

};

 

// añade la animación al storyboard.

flip.Children.Add(animation);

 

//crea una proyección de la imagen si no tiene ninguna

if (image.Projection == null)

{

// Establece el centro de rotación en -0.01,

//lo que proporcionará un espacio entre las imagenes

//cuando estas se giren.

image.Projection = new PlaneProjection()

{

CenterOfRotationX = -0.01

};

}

 

PlaneProjection projection = image.Projection as PlaneProjection;

 

// Establece el origen y destino de la imagen basandose

//en la capa de las imagenes

if (projection.RotationY == 0)

{

animation.To = 180;

}

else

{

animation.From = 180;

animation.To = 0;

}

 

//le indica cuando se produce la animación que imagen debe de situarse

//en cada plano

Storyboard.SetTarget(animation, projection);

 

// le indica a la animación la propiedad RotationYproperty

//qeu debe de tomar

Storyboard.SetTargetProperty(animation,

new PropertyPath(PlaneProjection.RotationYProperty));

 

flip.Begin();

}

 

 

}

Lo primero que hago es indicar un índice a través de la variable Z, de este modo se va mover la imagen, asegurando que la imagen que realiza el movimiento será la primera y única foto visible en el margen izquierdo.

Ahora me centraré en el evento FlipImage, lo primero es crear la animación, indicando la duración de la misma, en este caso un segundo. El siguiente paso es añadir la animación al StoryBoard, de modo que se cree una rotación cuyo centro será -0.01, así se creará un pequeño espacio entre la imagen que se está rotando y la imagen estática. La rotación se realiza a través del elemento PlaneProjection aunque podemos utilizar otros objetos más sofisticados a la par de nuestras necesidades.

Como podeis ver una pequeña aplicación le podemos dar un toque de sofisticación, mejorando la experiencia de usuario.

Depurando Data Binding

Uno de los problemas con los que me encuentro a menudo, son las limitaciones que ofrece el lenguaje XAML a la hora de obtener información sobre un error. Voy a centrarme en la depuración de los Data Binding, debido a que son una parte importante de Windows Presentation Foundation y la detección de errores de los mismos se hace casi indispensable.

Tengo dos métodos para depurar los Binding:

  • Output Window de Visual Studio

     

    Tengo una aplicación que carga una serie de imágenes de un archivo XML, pero he utilizado mal el nombre de la propiedad que mostrará las distintas imágenes.

    <Image Source=»{Binding Path=PictureWrong}» Width=»300″ Height=»300″ Grid.Row=»0″/>

    Cada vez que no se vincula de forma correcta una imagen con la propiedad correspondiente se sobreimpresiona un mensaje de error del tipo:

    System.Windows.Data Error: 35 : BindingExpression path error: ‘PictureWrong’ property not found on ‘object’ «Star’ (HashCode=49535530)’. BindingExpression:Path=PictureWrong; DataItem=’Star’ (HashCode=49535530); target element is ‘Image’ (Name=»); target property is ‘Source’ (type ‘ImageSource’)

    De este modo visual Studio, me presenta la información necesaria para reconocer que la propiedad que enlazamos a una fuente de datos «Source», tomando los datos de forma inadecuada.

     

  • Trace Level

     

    Esta opción aparece en el Framework 3.5 y ayuda más aún a obtener al detalle los diferentes errores que se producen en los Data Binding. Por ejemplo tenemos un «TextBox», cuya propiedad «Text» será cambiada por el valor que le otorga la propiedad «Value» del control Slider a través de un Data Binding.

    <TextBox Text=»{Binding ElementName=slider, Path=Value, Mode=OneWay}» Height=»30″ Width=»100″ Margin=»10″ />

<Slider Minimum=»0″ Maximum=»100″ Value=»20″ Margin=»10″ x:Name=»slider» />

Para obtener toda la información y que esta se muestre en la output Window, debo introducir el espacio de nombres «System.Diagnostics» y en el Binding he de añadir «PresentationTraceSources.TraceLevel=High»

 

    <Window x:Class=»WpfApplication1.Window1″

        xmlns=»http://schemas.microsoft.com/winfx/2006/xaml/presentation»

        xmlns:x=»http://schemas.microsoft.com/winfx/2006/xaml»

        xmlns:trace=»clr-namespace:System.Diagnostics;assembly=WindowsBase»

        Title=»Window1″ Height=»300″ Width=»300″>

        <Grid>

<TextBox Text=»{Binding ElementName=slider, Path=Value, Mode=OneWay,trace:PresentationTraceSources.TraceLevel=High}» Height=»30″ Width=»100″ Margin=»10″/>

    <Slider Minimum=»0″ Maximum=»100″ Value=»20″ Margin=»10″ x:Name=»slider» />

        </Grid>

</Window>

 

La ventana de depuración obtendrá resultados como este:

Created BindingExpression (hash=17654054) for Binding (hash=44624228)
Path: ‘Value’
BindingExpression (hash=17654054): Default update trigger resolved to LostFocus
BindingExpression (hash=17654054): Attach to System.Windows.Controls.TextBox.Text (hash=52727599)
BindingExpression (hash=17654054): Resolving source
BindingExpression (hash=17654054): Found data context element: (OK)
Lookup name slider: queried TextBox (hash=52727599)
BindingExpression (hash=17654054): Resolve source deferred

BindingExpression (hash=17654054): Resolving source
BindingExpression (hash=17654054): Found data context element: (OK)
Lookup name slider: queried TextBox (hash=52727599)
BindingExpression (hash=17654054): Activate with root item Slider (hash=54371668)
BindingExpression (hash=17654054): At level 0 – for Slider.Value found accessor DependencyProperty(Value)
BindingExpression (hash=17654054): Replace item at level 0 with Slider (hash=54371668), using accessor DependencyProperty(Value)
BindingExpression (hash=17654054): GetValue at level 0 from Slider (hash=54371668) using DependencyProperty(Value): ’20’
BindingExpression (hash=17654054): TransferValue – got raw value ’20’
BindingExpression (hash=17654054): TransferValue – implicit converter produced ’20’
BindingExpression (hash=17654054): TransferValue – using final value ’20’

 

 

 

Multiselección en un ListBox de Silverlight 3

Una de las propiedades que se han introducido en la nueva versión de Silverlight 3, es la posibilidad de realizar varias selecciones (Multi-select), de los diferentes Items del control ListBox.

Para comprender dicha novedad voy a crear un pequeño ejemplo, que estará formado por el control citado. Elegiré las opciones que crea convenientes y estas serán presentadas a través de un Texblock, cuando presione en el botón correspondiente.

Lo primero que crearé serán los distintos controles de la interfaz de usuario:

<UserControl x:Class="MultiSelectLB.MainPage"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

Width="500" Height="280">

 

<Grid x:Name="LayoutRoot"

Background="wheat"

Margin="10">

<Grid.ColumnDefinitions>

<ColumnDefinition Width="1*" />

<ColumnDefinition Width="1*" />

</Grid.ColumnDefinitions>

<Grid.RowDefinitions>

<RowDefinition Height="1.5*" />

<RowDefinition Height="1*" />

<RowDefinition Height="1*" />

<RowDefinition Height="*" />

</Grid.RowDefinitions>

<TextBlock x:Name="Titulo"

Margin="0"

Text="Multi-selección ListBox"

Foreground="Red"

FontFamily="Georgia"

FontSize="32"

HorizontalAlignment="Center"

VerticalAlignment="Center"

Grid.ColumnSpan="2" />

<ListBox x:Name="Multisel"

Margin="5"

Grid.Row="1"

Grid.Column="0"

SelectionMode="Multiple" />

<Button x:Name="readyButton"

Margin="5"

Grid.Row="1"

Grid.Column="1"

Width="100"

Height="35"

Content="Pulsar para elegir" />

<TextBlock x:Name="Mensaje"

HorizontalAlignment="Left"

Margin="5"

Grid.Row="2"

Grid.Column="0"

Grid.ColumnSpan="2"

Foreground="Blue"

VerticalAlignment="Bottom"

FontFamily="Georgia"

FontSize="18" />

 

</Grid>

 

</UserControl>

 

Como podemos ver en el control ListBox a la hora de indicar el modo de seleccinar los items, podemos elegir entre 3 opciones:

  • Single
    

  • Multiple
    

  • Extended
    

     

    12

El siguiente paso será crear una nueva clase (SelectItem), en la que habrá una lista con los nombres de los distintos Items que puedo elegir. Crearemos un método para comprobar que se ha seleccionado algún item y si es así presentarlo en el control Textblock creado con anterioridad:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Windows.Controls;

 

namespace MultiSelectLB

 

{

 

public partial class MainPage : UserControl

 

{

 

private readonly List<string> Colores =new List<string> { "Rojo", "Amarillo", "Verde", "Azul", "Negro", "Blanco" };

 

public MainPage()

 

{

 

InitializeComponent();

msLB.ItemsSource = Colores;

readyButton.Click += BClick;

 

}

 

private void BClick(object sender, System.Windows.RoutedEventArgs e)

 

{

 

var selectColor = msLB.SelectedItems;

Message.Text = string.Empty;

foreach (string NombreColor in selectColor)

{

 

if (Mensaje.Text.Length > 0)

{

Mensaje.Text += ", ";

}

Mensaje.Text += NombreColor;

 

}

}

 

}

 

}

 

En conclusión, el plus que nos da Silverlight en el ListBox respecto a versiones anteriores, permite realizar la acción de selección de Items en un solo paso. Con lo que se reduce la redundancia de procesos y la consiguiente monotonía para el usuario, creando un aumento de productividad de nuestra aplicación.

 

Acelerar animaciones a través de GPU

Muchas veces en las aplicaciones web notamos como algunas animaciones tardan en ejecutarse o no lo hacen de la forma adecuada. En este articulo os mostraré como una animación puede acelerarse a través de la GPU de nuestro equipo incluso aplicándole efectos que son lentos en su ejecución.

Para realizar la prueba contamos con una animación en la que podemos ver los efectos que se producen al utilizar la aceleración a través de GPU, lo puedes comprobar aquí.

Para mostraros esta aceleración empezaremos permitiendo a todo el Plugin de Silverlight3 que acelere la animación a través de GPU. Para ellos hemos de agregar un parámetro a la etiqueta <object> del archivo HTML que tiene nuestra aplicación Silverlight3.

<object data=«data:application/x-silverlight,» type=«application/x-silverlight-3»

width=«100%» height=«100%»>

<param name=«enableGPUAcceleration» value=«true» />

En Windows la aceleración GPU funciona tanto para pantalla completa como para pantalla reducida. En Mac OSX solo funciona para pantalla completa.Los usuarios de Windows deben de tener una tarjeta gráfica compatible con DirectX 9, además de sus correspondientes controladores.Los usuarios de Mac necesitarán una tarjeta compatible con OpenGl2.

Ahora la acción que nos concierne la aplicaremos sobre un elemento XAML de Silverlight3,en este caso un CANVAS.Este será a su vez contenedor de los diferentes controles que se incluiran más tarde (Imágenes, animaciones,pinceles,etc.), heredando dicha capacidad los controles contenidos dentro del Canvas.

<Canvas CacheMode=»BitmapCache» />

Ahora os preguntareis pero ¿la aceleración no es a través de GPU en vez de Bitmap? Para entender esto como podeis ver no hemos utilizado implicitamente «UseGPU» o «AccelerateThisALotUsingGPU», porque en Silverlight 3 La aceleración de GPU no es ni más ni menos que una aceleración de un Bitmap en la caches de la GPU.

El siguiente paso es otorgar mayor velocidad a la hora de presentar nuestros pixeles, para ello incluiremos el siguiente código en la etiqueta <object> del HTML:

<param name=»enableFramerateCounter» value=»True» />

Cabe destacar que no todas las tarjetas de video tienen la misma cantidad de memoria, con lo que cuando esta se acaba Silverlight3 ejecuta sus acciones a través del Software, ralentizando los procesos de la aplicación.

Para solucionar este problema es aconsejable que actives la visualización de caché. Este añadido permitirá ver qué elementos son acelerados mediante GPU y cuáles no. Solo debemos incluir el siguiente código el HTML de la página que se va alojar o en el código Behing en tiempo de ejecución:

<param name=»enableCacheVisualization» value=»true» />

El camino que hemos de seguir es hacia el escalado de nuestra animación. Para lograr este objetivo debo de añadir la propiedad RenderAtScale del objeto BitmapCache:

var bmc = new BitmapCache();

bmc.RenderAtScale = 4;

lion1.CacheMode = bmc;//siendo lion1 nuestra animación

El último paso es configurar RenderAtScale en el XAML:

<Canvas>

<Canvas.CacheMode>

<BitmapCache RenderAtScale=»4″ />

</Canvas.CacheMode>

</Canvas>

El código anteriormente introducido, nos permite escalar los vectores de Silverlight a 4X, esto conlleva un aumento de la memoria 16 veces más de lo que anteriormente tenía, con lo que la animación se ralentizará si no se dispone de una buena tarjeta de video.

En conclusión la utilización de la aceleración por GPU es una buena ayuda para acelerar nuestras animaciones. Esta acción tienes sus ventajas y desventajas y es bueno saber que se ha de aplicar a animaciones que su contenido cambia rara vez, debido a que el cambio de Bitmap se realiza en la caché de la GPU.

Si desea obtener más información sobre este tema puede consultar el blog de András Velvárt