Windows Phone 7 – Tutorial XXIV–Touch Input IV

clip_image003

En los anteriores artículos hemos visto los gestos Tap, DoubleTap, Pan y Flick, en este nos vamos a centrar en Touch and Hold, cuando realizamos aplicaciones para Escritorio solemos utilizar el botón derecho del ratón para sacar generalmente un menú contextual, en los dispositivos tactiles al no tener ratón este evento se simula al pulsar en un lugar durante un determinado tiempo, siguiendo los ejemplos anteriores vamos a construir un behaivor que simule este gesto, lo vamso a llamar TouchAndHoldAction que podremos aplicar a cualquier UIElement.

1 public class TouchAndHoldAction : DoubleTapAction 2 { 3 public event EventHandler TouchAndHold; 4 5 private Point? TapLocation; 6 private DispatcherTimer timer = new DispatcherTimer(); 7 8 public int HoldTimeoutInMilliseconds 9 { 10 get { return (int)GetValue(HoldTimeoutInMillisecondsProperty); } 11 set { SetValue(HoldTimeoutInMillisecondsProperty, value); } 12 } 13 14 public static readonly DependencyProperty HoldTimeoutInMillisecondsProperty = 15 DependencyProperty.Register("HoldTimeoutInMilliseconds", 16 typeof(int), typeof(TouchAndHoldAction), 17 new PropertyMetadata(2000)); 18 19 public int Tolerance 20 { 21 get { return (int)GetValue(ToleranceProperty); } 22 set { SetValue(ToleranceProperty, value); } 23 } 24 25 public static readonly DependencyProperty ToleranceProperty = 26 DependencyProperty.Register("Tolerance", typeof(int), 27 typeof(TouchAndHoldAction), 28 new PropertyMetadata(2)); 29 30 protected override void OnAttached() 31 { 32 base.OnAttached(); 33 34 this.AssociatedObject.MouseLeftButtonDown += AO_MouseLeftButtonDown; 35 this.AssociatedObject.MouseMove += AO_MouseMove; 36 this.timer.Tick += timer_Tick; 37 } 38 39 protected override void OnDetaching() 40 { 41 this.AssociatedObject.MouseLeftButtonDown -= AO_MouseLeftButtonDown; 42 this.AssociatedObject.MouseMove -= AO_MouseMove; 43 this.timer.Tick -= timer_Tick; 44 45 base.OnDetaching(); 46 } 47 48 void AO_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) 49 { 50 var pos = e.GetPosition(null); 51 OnTouchAndHoldStarted(pos); 52 } 53 54 void AO_MouseMove(object sender, MouseEventArgs e) 55 { 56 if (TapLocation.HasValue) 57 { 58 var pos = e.GetPosition(null); 59 if (Math.Abs(TapLocation.Value.X - pos.X) > Tolerance || 60 Math.Abs(TapLocation.Value.Y - pos.Y) > Tolerance) 61 { 62 OnTouchAndHoldStarted(pos); 63 } 64 } 65 } 66 67 void timer_Tick(object sender, EventArgs e) 68 { 69 OnTouchAndHoldCompleted(); 70 timer.Stop(); 71 TapLocation = null; 72 } 73 74 protected override void OnTap() 75 { 76 timer.Stop(); 77 base.OnTap(); 78 } 79 80 protected virtual void OnTouchAndHoldStarted(Point pt) 81 { 82 TapLocation = pt; 83 84 timer.Stop(); 85 timer.Interval = new TimeSpan(0, 0, 0, 0, HoldTimeoutInMilliseconds); 86 timer.Start(); 87 } 88 protected virtual void OnTouchAndHoldCompleted() 89 { 90 MouseDown = false; 91 if (TouchAndHold != null) 92 { 93 TouchAndHold(this.AssociatedObject, EventArgs.Empty); 94 } 95 } 96 }

Este behaivor tiene la propiedad HoldTimeoutInMilliseconds que le indicaremos el tiempo en milisegundos que el usuario tiene que tener presionado, si pasa este tiempo lanzara el evento TouchAndHold, luego he puesto la propiedad Tolerance que permitirá al usuario poder desplazar el dedo durante los pixels que le indiquemos en esta propiedad si mueve el demo mas de esta longitud anularemos el vento.

Para el ejemplo vamos a utilizar el código del anterior artículo y pondremos este gesto en todos los borders redondeados que teníamos, el XAML sería

1 <Border HorizontalAlignment="Left" Height="368" Margin="19,231,0,0" VerticalAlignment="Top" Width="441" BorderBrush="White" BorderThickness="2" Background="#FF313131"> 2 <Canvas Background="Transparent"> 3 <Canvas.Resources> 4 <Style x:Key="RoundedBorder" TargetType="Border"> 5 <Setter Property="BorderThickness" Value="3"/> 6 <Setter Property="CornerRadius" Value="20"/> 7 <Setter Property="Background" Value="#FF287E3D"/> 8 <Setter Property="BorderBrush" Value="#FF0F451C"/> 9 <Setter Property="Width" Value="25"/> 10 <Setter Property="Height" Value="25"/> 11 </Style> 12 </Canvas.Resources> 13 <i:Interaction.Behaviors> 14 <!--<local:PanAction />--> 15 <!--<local:FlickAction />--> 16 <!--<local:ZoomAction />--> 17 </i:Interaction.Behaviors> 18 <Border Canvas.Left="43" Canvas.Top="137" Style="{StaticResource RoundedBorder}" > 19 <i:Interaction.Behaviors> 20 <local:TouchAndHoldAction TouchAndHold="TouchAndHoldAction_TouchAndHold" /> 21 </i:Interaction.Behaviors> 22 </Border> 23 <Border Canvas.Left="122" Canvas.Top="83" Style="{StaticResource RoundedBorder}" > 24 <i:Interaction.Behaviors> 25 <local:TouchAndHoldAction TouchAndHold="TouchAndHoldAction_TouchAndHold" /> 26 </i:Interaction.Behaviors> 27 </Border> 28 <Border Canvas.Left="294" Canvas.Top="301" Style="{StaticResource RoundedBorder}" > 29 <i:Interaction.Behaviors> 30 <local:TouchAndHoldAction TouchAndHold="TouchAndHoldAction_TouchAndHold" /> 31 </i:Interaction.Behaviors> 32 </Border> 33 <Border Canvas.Left="273" Canvas.Top="51" Style="{StaticResource RoundedBorder}" > 34 <i:Interaction.Behaviors> 35 <local:TouchAndHoldAction TouchAndHold="TouchAndHoldAction_TouchAndHold" /> 36 </i:Interaction.Behaviors> 37 </Border> 38 <Border Canvas.Left="74" Canvas.Top="242" Style="{StaticResource RoundedBorder}" > 39 <i:Interaction.Behaviors> 40 <local:TouchAndHoldAction TouchAndHold="TouchAndHoldAction_TouchAndHold" /> 41 </i:Interaction.Behaviors> 42 </Border> 43 <Border Canvas.Left="236" Canvas.Top="167" Style="{StaticResource RoundedBorder}" > 44 <i:Interaction.Behaviors> 45 <local:TouchAndHoldAction TouchAndHold="TouchAndHoldAction_TouchAndHold" /> 46 </i:Interaction.Behaviors> 47 </Border> 48 </Canvas> 49 </Border>

 

Como podéis ver en el XAML enlazamos el behaivor y lo enlazamos al evento TouchAndHoldAction_TouchAndHold en este evento cambiaremos el colñor de los puntos, quedándonos

image

 

MyNikeplus – Mi primera Aplicación en el Marketplace de Windows Phone 7

Toda la gente que me conoce sabe que correr es una de mis aficiones preferidas y aunque ahora no puedo hacerlo tan habitualmente (la edad, trabajo, niño…), no sigo planes de entrenamiento, ni me planteo objetivos.. sigo saliendo a correr en cuento tengo oportunidad. Como soy poseedor de hace tiempo de Nikeplus aunque no lo utilizo ahora mismo, pensé que mi primera aplicación para Windows Phone 7 podría ser una aplicación que me mostrase todos los datos registrados en Nikeplus en el móvil de Windows Phone 7. Así que me plantee ir aprendiendo los conceptos de Windows Phone 7 e ir publicando artículos mientras iba haciendo la aplicación, además he intentado registrar todo el tiempo consumido (horas libres a retazos) para ver la productividad del entorno y una vez hecha las cuentas mi impresión es que es un entorno realmente productivo.

La aplicación no es excesivamente grande ni compleja, lo mas complejo ha sido localizar si Nikeplus exponía su API y obtener los datos de su API, por desgracia Nikeplus no tiene ninguna documentación y ha sido los mas complejo del proyecto junto con la tarea de certificarme como desarrollador. En total he consumido 92 horas mas o menos, si hacemos cálculos 11 días y medio de una jornada laboral de 8 horas que si se hubiera desarrollado así seguro que habrían sido menos horas, me quedo con la conclusión que podemos realizar aplicaciones para Windows Phone 7 sin invertir mucho esfuerzo.

 

La aplicación se llama MyNikePlus y os dejo un video (pequeñito) de un minuto y poco para que echéis un vistazo a la aplicación

 

Windows Phone 7 – Tutorial XXIII–Touch Input III

En el anterior articulo vimos los dos primeros gestos Tap y Double-Tap, en este vamos a profundizar en los gestos  Pan y Flick.

Pan

clip_image004

 

Este gesto es totalmente diferente a los vistos anteriores el usuario mueve el dedo a través de la pantalla, es como cuando hemos realizado Drag & Drop en aplicaciones de PC, como si el dedo fuese el ratón y controlásemos el evento  MouseMove. Siguiendo la idea que hemos realizado con los dos anteriores gestos vamos a realizar un behaivor al que denominaremos PanAction que nos permita obtener este gesto del usuario y sea redirigido a un evento, la única diferencia con los anteriores es que lo delimitaremos al objeto Canvas  y moveremos todos los hijos del canvas en la dirección que mueva el dedo el usuario, en definitiva implementaremos un Drag&Drop donde el usuario podrá simular los movimientos que vemos en los siguientes dibujos

image

El código correspondiente a este behaivor sería el siguiente

 

1 public class PanAction : Behavior<Canvas> 2 { 3 protected Point? MouseDown; 4 protected UIElement SelectedItem { get; set; } 5 6 protected override void OnAttached() 7 { 8 base.OnAttached(); 9 10 this.AssociatedObject.MouseLeftButtonDown += AO_MouseLeftButtonDown; 11 this.AssociatedObject.MouseLeftButtonUp += AO_MouseLeftButtonUp; 12 this.AssociatedObject.MouseMove += AO_MouseMove; 13 } 14 15 protected override void OnDetaching() 16 { 17 this.AssociatedObject.MouseLeftButtonDown -= AO_MouseLeftButtonDown; 18 this.AssociatedObject.MouseLeftButtonUp -= AO_MouseLeftButtonUp; 19 this.AssociatedObject.MouseMove -= AO_MouseMove; 20 21 base.OnDetaching(); 22 } 23 24 private void AO_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) 25 { 26 this.MouseDown = e.GetPosition(null); 27 this.SelectedItem = VisualTreeHelper.FindElementsInHostCoordinates(e.GetPosition(null), this.AssociatedObject).FirstOrDefault(); 28 if (this.SelectedItem == this.AssociatedObject) 29 { 30 this.SelectedItem = null; 31 } 32 } 33 34 protected virtual void AO_MouseMove(object sender, MouseEventArgs e) 35 { 36 var pos = e.GetPosition(null); 37 var xdiff = pos.X - MouseDown.Value.X; 38 var ydiff = pos.Y - MouseDown.Value.Y; 39 if (MouseDown.HasValue) 40 { 41 MoveSelectedItems(xdiff, ydiff); 42 } 43 MouseDown = pos; 44 } 45 46 private void AO_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) 47 { 48 this.SelectedItem = null; 49 this.MouseDown = null; 50 } 51 52 protected void MoveSelectedItems(double xdiff, double ydiff) 53 { 54 if (this.SelectedItem != null) 55 { 56 MoveItem(this.SelectedItem, xdiff, ydiff); 57 } 58 else 59 { 60 foreach (var child in this.AssociatedObject.Children) 61 { 62 MoveItem(child, xdiff, ydiff); 63 } 64 } 65 } 66 67 private void MoveItem(UIElement item, double xdiff, double ydiff) 68 { 69 var left = Canvas.GetLeft(item) + xdiff; 70 left = Math.Min(Math.Max(0, left), this.AssociatedObject.ActualWidth); 71 var top = Canvas.GetTop(item) + ydiff; 72 top = Math.Min(Math.Max(0, top), this.AssociatedObject.ActualHeight); 73 Canvas.SetLeft(item, left); 74 Canvas.SetTop(item, top); 75 } 76 }

Como podeis observar en el código hemos asociado el behaivor a los objetos Canvas y a la hora de”Atacharlo” nos subscribimos a los eventos MouseLeftButtonDown, MouseLeftButtonUp, MouseMove con estos tres eventos tenemos todo lo necesario para simular el Drag&Drop, lo importante esta en el evento MouseLeftButtonDown donde obtenemos los objetos hijos del Canvas a través del método

1 this.SelectedItem = VisualTreeHelper.FindElementsInHostCoordinates(e.GetPosition(null), this.AssociatedObject).FirstOrDefault();


Una vez recogidos los objetos que el usuario ha “tocado” en el evento MouseMove solo deberemos moverlos. Para probar este behaivor vamos a extender el proyecto anterior para tener un Canvas con puntos verdes que se podrán mover con este behaivor.

 

1 <Border HorizontalAlignment="Left" Height="368" Margin="19,231,0,0" VerticalAlignment="Top" Width="441" BorderBrush="White" BorderThickness="2" Background="#FF313131"> 2 <Canvas Background="Transparent"> 3 <Canvas.Resources> 4 <Style x:Key="RoundedBorder" TargetType="Border"> 5 <Setter Property="BorderThickness" Value="3"/> 6 <Setter Property="CornerRadius" Value="20"/> 7 <Setter Property="Background" Value="#FF287E3D"/> 8 <Setter Property="BorderBrush" Value="#FF0F451C"/> 9 <Setter Property="Width" Value="25"/> 10 <Setter Property="Height" Value="25"/> 11 </Style> 12 </Canvas.Resources> 13 <i:Interaction.Behaviors> 14 <local:PanAction /> 15 16 </i:Interaction.Behaviors> 17 <Border Canvas.Left="43" Canvas.Top="137" Style="{StaticResource RoundedBorder}" > 18 </Border> 19 <Border Canvas.Left="122" Canvas.Top="83" Style="{StaticResource RoundedBorder}" > 20 </Border> 21 <Border Canvas.Left="294" Canvas.Top="301" Style="{StaticResource RoundedBorder}" > 22 </Border> 23 <Border Canvas.Left="273" Canvas.Top="51" Style="{StaticResource RoundedBorder}" > 24 </Border> 25 <Border Canvas.Left="74" Canvas.Top="242" Style="{StaticResource RoundedBorder}" > 26 </Border> 27 <Border Canvas.Left="236" Canvas.Top="167" Style="{StaticResource RoundedBorder}" > 28 </Border> 29 </Canvas> 30 </Border>

 

Obtendríamos la siguiente pantalla

 

image

 

Flick

clip_image005

 

Conceptualmente hay dos tipos de Flick uno empieza cuando el usuario toca la pantalla y el otro ocurre al final de un Pan, sin embargo si trsladamos esto a código podemos pensar que ambos ocurren al final de un pan, para el primero es un pan con distancia cero, por esta razón voy a construir el behaivor para el gesto Flick a partir del behaivor Pan.

Le vamos a denominar FlickAction y para detectar esta acción deberemos saber la velocidad con la que el dedo del usuario estaba moviéndose en el momento que soltó el dedo de la pantalla.  Los eventos de Mouse que hemos ido utilizando no nos sirven ya que no nos ofrecen este detalle. Windows Phone expone otros eventos diferentes a los que tiene Silverlight 3, estos eventos nos proporciona información asociada a los gestos del usuario ManipulationStarted , ManipulationDelta , y ManipulationCompleted . ManipulationStarted es lanzado cuando comienza un evento de “manipulación”, es decir, el usuario toca la pantalla en uno o mas puntos, cuando el usuario cambia de posición el dedo se lanza el evento ManipulationDelta, por último cuando se suelta uno o mas dedos de la pantalla se lanza el evento  ManipulationCompleted 

Para nuestro behaivor deberemos de implementar el evento ManipulationCompleted porque expone la propiedad FinalVelocities que nos indica la velocidad (en unidades de pantalla por segundo) que el usuario soltó el dedo de la pantalla. Para este behaivor vamos a simular un drift effect muy sencillo sin implementar fisica.

Este behaivor expondrá la propiedad FlickDurationInMilliseconds que sera la cantidad de tiempo que el control continuara moviéndose despues de que el usuario suelte el dedo.

 

1 public class FlickAction : PanAction 2 { 3 private const int Increments = 10; 4 private const double Deceleration = 0.4; 5 6 DispatcherTimer timer = new DispatcherTimer(); 7 8 public int Counter { get; set; } 9 10 private Point ReleaseVelocity { get; set; } 11 12 public int FlickDurationInMilliseconds 13 { 14 get { return (int)GetValue(FlickDurationInMillisecondsProperty); } 15 set { SetValue(FlickDurationInMillisecondsProperty, value); } 16 } 17 18 public static readonly DependencyProperty FlickDurationInMillisecondsProperty = 19 DependencyProperty.Register("FlickDurationInMilliseconds", 20 typeof(int), typeof(FlickAction), 21 new PropertyMetadata(500)); 22 23 public FlickAction() 24 { 25 timer.Tick += new EventHandler(timer_Tick); 26 } 27 28 protected override void OnAttached() 29 { 30 base.OnAttached(); 31 this.AssociatedObject.ManipulationStarted += AO_ManipulationStarted; 32 this.AssociatedObject.ManipulationCompleted += AO_ManipulationCompleted; 33 } 34 35 protected override void OnDetaching() 36 { 37 this.AssociatedObject.ManipulationStarted -= AO_ManipulationStarted; 38 this.AssociatedObject.ManipulationCompleted -= AO_ManipulationCompleted; 39 base.OnDetaching(); 40 } 41 42 void timer_Tick(object sender, EventArgs e) 43 { 44 Counter--; 45 if (Counter == 0) 46 { 47 timer.Stop(); 48 } 49 50 MoveSelectedItems(ReleaseVelocity.X / 100, ReleaseVelocity.Y / 100); 51 52 ReleaseVelocity = new Point(ReleaseVelocity.X * Deceleration, 53 ReleaseVelocity.Y * Deceleration); 54 } 55 56 void AO_ManipulationStarted(object sender, ManipulationStartedEventArgs e) 57 { 58 timer.Stop(); 59 } 60 61 void AO_ManipulationCompleted(object sender, 62 ManipulationCompletedEventArgs e) 63 { 64 ReleaseVelocity = e.FinalVelocities.LinearVelocity; 65 timer.Interval = new TimeSpan(0, 0, 0, 0, 66 FlickDurationInMilliseconds / Increments); 67 Counter = Increments; 68 timer.Start(); 69 } 70 }

El XAML en este caso es el mismo que el anterior solo que cambiamos el behaivor PanAction por FlickAction

Windows Phone 7 – Tutorial XXII–Touch Input II

Después de dos semanas sin escribir nada debido a que hemos estado a tope con el trabajo, vamos a ver en este post parte de los eventos que vimos en el articulo anterior.

Tap

clip_image001

Este evento funciona como si tuviésemos un ratón en nuestro dedo y reacciona al simple toque de nuestro dedo en la pantalla, como supondréis este gesto viene implementado en muchos controles, por ejemplo el control Button lo expone a través del evento Click 

< Button Content=”Simple Gestures” Name=”SimpleGestureButton” Click=”SimpleGestureButton_Click” / >

Sin embargo no todos los controles exponen el gesto Tap, por ejemplo el elemento Border que hereda de Panel no expone el evento Click, si seguimos el árbol de herencia veremos que llegaremos al elemento UIElement el cual expone los eventos MouseLeftButtonDown y MouseLeftButtonUp . Para simular el gesto de Tap con estos dos eventos podríamos utilizar un behaivor que nos permita reusarlo para cualquier elemento.

Para realizar un behaivor podéis ver este articulo que aunque sea de WPF es lo mismo tanto para Silverlight como WPF, recordar que para realizar un behaivor debemos de añadir la referencia al assemblie System.Windows.Interactivity.dll, el behaivor lo enlazaremos a cualquier elemento de la clase UIElement y lo que hará será lanzar un evento Tap jugando con los dos eventos anteriores, aquí tenéis  el código.

1 public class TapAction : Behavior<UIElement> 2 { 3 public event EventHandler Tap; 4 5 protected bool MouseDown { get; set; } 6 7 protected override void OnAttached() 8 { 9 base.OnAttached(); 10 11 this.AssociatedObject.MouseLeftButtonDown += AO_MouseLeftButtonDown; 12 this.AssociatedObject.MouseLeftButtonUp += AO_MouseLeftButtonUp; 13 } 14 15 protected override void OnDetaching() 16 { 17 this.AssociatedObject.MouseLeftButtonDown -= AO_MouseLeftButtonDown; 18 this.AssociatedObject.MouseLeftButtonUp -= AO_MouseLeftButtonUp; 19 20 base.OnDetaching(); 21 } 22 23 void AO_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) 24 { 25 if (MouseDown) 26 { 27 OnTap(); 28 } 29 MouseDown = false; 30 } 31 32 void AO_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) 33 { 34 MouseDown = true; 35 } 36 37 protected virtual void OnTap() 38 { 39 if (Tap != null) 40 { 41 Tap(this.AssociatedObject, EventArgs.Empty); 42 } 43 } 44 }

Luego en cualquier elemento como un Border se lo asociaremos por XAML

 

1 <phone:PhoneApplicationPage 2 x:Class="TouchInput.MainPage" 3 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 4 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 5 xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone" 6 xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone" 7 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 8 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 9 xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" 10 xmlns:local="clr-namespace:TouchInput.Helper" 11 mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768" 12 FontFamily="{StaticResource PhoneFontFamilyNormal}" 13 FontSize="{StaticResource PhoneFontSizeNormal}" 14 Foreground="{StaticResource PhoneForegroundBrush}" 15 SupportedOrientations="Portrait" Orientation="Portrait" 16 shell:SystemTray.IsVisible="True"> 17 18 <!--LayoutRoot is the root grid where all page content is placed--> 19 <Grid x:Name="LayoutRoot" Background="Transparent"> 20 <Grid.RowDefinitions> 21 <RowDefinition Height="Auto"/> 22 <RowDefinition Height="*"/> 23 </Grid.RowDefinitions> 24 25 <!--TitlePanel contains the name of the application and page title--> 26 <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="24,24,0,12"> 27 <TextBlock x:Name="ApplicationTitle" Text="BUILT TO ROAM" Style="{StaticResource PhoneTextNormalStyle}"/> 28 <TextBlock x:Name="PageTitle" Text="flick tap pane" Margin="-3,-8,0,0" Style="{StaticResource PhoneTextTitle1Style}"/> 29 </StackPanel> 30 31 <!--ContentPanel - place additional content here--> 32 <Grid x:Name="ContentGrid" Grid.Row="1"> 33 <Border BorderBrush="#FFF11717" BorderThickness="2" Height="90" Margin="20,0,0,409" VerticalAlignment="Bottom" Background="#FFCC8787" HorizontalAlignment="Left" Width="160"> 34 <i:Interaction.Behaviors> 35 <local:TapAction Tap="TapGesture_Tap"/> 36 </i:Interaction.Behaviors> 37 </Border> 38 39 </Grid> 40 </Grid>

Para asociar el behaivor por Blend os dejo los siguientes pantallazos:

 

imageimageimage

 

Como podéis ver capturamos el evento Tap y en el code-behind

1 private void TapGesture_Tap(object sender, EventArgs e) 2 { 3 MessageBox.Show("Has hecho TAP en el Border!"); 4 }

De esta manera capturamos todos los eventos de Tap de una manera muy sencilla

 

Double Tap

clip_image002

 

Recordamos que el Double-Tap son dos gestos Tap en un periodo de tiempo muy corto, no existe este evento en ningún objeto con lo que aprovecharemos el behaivor anterior para extenderlo para detectar estos dos Tap, incluiremos una propiedad donde indicaremos el tiempo que queremos que pase entre gesto y gesto, a este behaivor le llamaremos DoubleTapAction, os dejo el código a continuación.

1 public class DoubleTapAction : TapAction 2 { 3 public event EventHandler DoubleTap; 4 5 public int DoubleTapTimeoutInMilliseconds 6 { 7 get { return (int)GetValue(DoubleTapTimeoutInMillisecondsProperty); } 8 set { SetValue(DoubleTapTimeoutInMillisecondsProperty, value); } 9 } 10 11 public static readonly DependencyProperty DoubleTapTimeoutInMillisecondsProperty = 12 DependencyProperty.Register("DoubleTapTimeoutInMilliseconds", typeof(int), 13 typeof(DoubleTapAction), new PropertyMetadata(1000)); 14 15 protected DateTime? FirstTap { get; set; } 16 17 protected override void OnTap() 18 { 19 base.OnTap(); 20 21 if (FirstTap.HasValue && 22 FirstTap.Value.AddMilliseconds(DoubleTapTimeoutInMilliseconds) > DateTime.Now) 23 { 24 OnDoubleTap(); 25 FirstTap = null; 26 } 27 else 28 { 29 FirstTap = DateTime.Now; 30 } 31 } 32 33 protected virtual void OnDoubleTap() 34 { 35 if (DoubleTap != null) 36 { 37 DoubleTap(this.AssociatedObject, EventArgs.Empty); 38 } 39 } 40 }

En el código anterior podéis ver que se ha definido la propiedad DoubleTapTimeoutInMilliseconds  que nos indicará el tiempo entre gestos Tap y se ha definido por defecto que sea de 1 segundo.

1 public class TapAction : Behavior<UIElement> 2 { 3 public event EventHandler Tap; 4 5 protected bool MouseDown { get; set; } 6 7 protected override void OnAttached() 8 { 9 base.OnAttached(); 10 11 this.AssociatedObject.MouseLeftButtonDown += AO_MouseLeftButtonDown; 12 this.AssociatedObject.MouseLeftButtonUp += AO_MouseLeftButtonUp; 13 } 14 15 protected override void OnDetaching() 16 { 17 this.AssociatedObject.MouseLeftButtonDown -= AO_MouseLeftButtonDown; 18 this.AssociatedObject.MouseLeftButtonUp -= AO_MouseLeftButtonUp; 19 20 base.OnDetaching(); 21 } 22 23 void AO_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) 24 { 25 if (MouseDown) 26 { 27 OnTap(); 28 } 29 MouseDown = false; 30 } 31 32 void AO_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) 33 { 34 MouseDown = true; 35 } 36 37 protected virtual void OnTap() 38 { 39 if (Tap != null) 40 { 41 Tap(this.AssociatedObject, EventArgs.Empty); 42 } 43 } 44 }

El XAML del nuevo Border sería

 

1 <Border BorderBrush="#FFF11717" BorderThickness="2" Margin="0,118,21,0" Background="#FFCC8787" HorizontalAlignment="Right" Width="160" Height="90" VerticalAlignment="Top"> 2 <i:Interaction.Behaviors> 3 <local:DoubleTapAction DoubleTap="DoubleTapGesture_DoubleTap"/> 4 </i:Interaction.Behaviors> 5 </Border>

1 <Border BorderBrush="#FFF11717" BorderThickness="2" Margin="0,118,21,0" Background="#FFCC8787" HorizontalAlignment="Right" Width="160" Height="90" VerticalAlignment="Top"> 2 <i:Interaction.Behaviors> 3 <local:DoubleTapAction DoubleTap="DoubleTapGesture_DoubleTap"/> 4 </i:Interaction.Behaviors> 5 </Border>

1 private void DoubleTapGesture_DoubleTap(object sender, EventArgs e) 2 { 3 MessageBox.Show("Has hecho Doucble-Tap en el Border!"); 4 }

En este articulo hemos vistos los dos primeros gestos, en los siguientes iremos desmenuzando los gestos que nos quedan, os dejo el código del proyecto

Windows Phone 7 – Tutorial XXI–Touch Input I

Desde que salió el iphone, ha quedado muy claro que los usuarios queremos teléfonos multitactiles con una buena respuesta y aplicaciones que estén diseñadas a las respuestas de nuestros gestos con los dedos. Windows Phone 7 tiene como requisito de hardware que los teléfonos deben de ser diseñados para tener esta característica y nosotros como programadores no debemos de obviarla.

Debemos de tener en cuenta UI Design and Interaction Guide for Windows Phone 7 para nuestros diseños, por ejemplo debemos de saber que el área mínima de un objeto visual para que el usuario interactúe con el “dedo” es de 26 pixels o 7mm y que el area de interacción de este objeto es de 9mm o 34 pixels.

Para que veáis dos objetos que el usuario va interactuar con ellos deberían de tener este tamaño

 

image

El objeto visual y un espacio alrededor de ellos ya que nuestro dedo no es tan preciso como una stylus y si los ponemos muy juntos el usuario tendría que ser muy preciso y lo que queremos es que sea fácil manejar la aplicación. Estos son los tamaños que Microsoft recomienda y que deberemos de tener en cuenta. Si nos fijamos en el teclado vemos claramente la separación entre los botones

 

image

 

Una vez que tenemos claro como debe de ser el tamaño de nuestros objetos, lo siguiente que tenemos que aprender son los gestos que son permitidos, estos gestos están ya definidos y son los siguientes.

 

Single-touch:

 

• Tap
• Double Tap
• Pan
• Flick
• Touch and Hold

Multi-touch:


• Pinch and Stretch

En la siguiente tabla podemos ver

Gesto

Descripción

Resultado en Windows phone

tap

clip_image001

Pulsa una vez en el elemento.

Abre e inicia todo lo que pulsas.

Double tap

clip_image002

Pulsa dos veces seguidas en el elemento.

Acerca o aleja paso a paso.

Touch and Hold

clip_image003

Pulsa y mantén pulsado el elemento durante unos segundos.

Abre un menú específico del contexto (como hacer clic con el botón secundario de un mouse).

Pan

clip_image004

Coloca el dedo en la pantalla y mantén el contacto mientras lo vas desplazando.

Se mueve por las pantallas o los menús a una velocidad controlada.

Flick

clip_image005

Desplaza el dedo rápidamente en la dirección en la que desees que se mueva la pantalla.

Te podrás desplazar rápidamente por menús o páginas, o moverte lateralmente en los hubs.

Pinch And Strech

clip_image006

Mueve los dedos pulgar e índice para juntarlos o separarlos sobre una pantalla.

Acerca o aleja paulatinamente un sitio web, un mapa o una imagen.

 

Una vez que ya sabemos como debemos diseñar nuestros objetos y los gestos que el usuario puede realizar, ahora solo debemos de capturarlos y realizar las acciones que diseñemos. Esto lo veremos en el siguiente post.