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