Touch event cancels Tap Gesture Recognizer in xamarin - xamarin

I am using SkiaSharp library to draw on canvas .
and I need to set a tap recognizer to a specific function when I double tap .
and the touch event of the canvas to do another functions .
each one works well separately but when I use them both , the Touch event cancels the Tap Recognizer .
is there is any way to use them both ?
<skia:SKCanvasView x:Name="canvasView"
PaintSurface="canvasView_paintSurface"
VerticalOptions="FillAndExpand"
EnableTouchEvents="true"
Touch="OnTouch">
<skia:SKCanvasView.GestureRecognizers >
<TapGestureRecognizer NumberOfTapsRequired="2" Tapped="OnTapped" >
</TapGestureRecognizer>
</skia:SKCanvasView.GestureRecognizers>
</skia:SKCanvasView>
private void OnTapped(object sender, EventArgs e)
{
DisplayAlert("hello", "OnTapped", "Ok", "Cancel");
}
private async void OnTouch(object sender, SKTouchEventArgs e)
{
DisplayAlert("hello", "OnTouch", "Ok", "Cancel");
}

One way of solving this is by creating two tap gesture recognizers and a timer to check the succession of the taps. This way you know whether there was a touch or a double tap
private bool tapHandled;
public XYZPage() : base()
{
var tgr = new TapGestureRecognizer();
tgr.NumberOfTapsRequired = 1;
tgr.Tapped += tapped;
GestureRecognizers.Add(tgr);
var ttgr = new TapGestureRecognizer();
ttgr.NumberOfTapsRequired = 2;
ttgr.Tapped += doubletapped;
GestureRecognizers.Add(ttgr);
}
private void tapped(object sender, EventArgs e)
{
tapHandled = false;
Xamarin.Forms.Device.StartTimer(new TimeSpan(0, 0, 0, 0, 300), taptimer);
}
private void doubletapped(object sender, EventArgs e)
{
tapHandled = true;
// do double tap work here
DisplayAlert("hello", "OnDoubleTapped", "Ok", "Cancel");
}
private bool taptimer()
{
if (!tapHandled)
{
tapHandled = true;
// do Touch stuff here
DisplayAlert("hello", "OnTouch", "Ok", "Cancel");
}
return false;
}

Related

Xamarin Modal Page Transition to Normal Page

Xamarin is having default page transition animation. Modal Page from bottom to top and Normal Page from right to left. Is there a way to apply modal page transition animation to a normal page.
I tried various CATransition animation in iOS using NavigationPageRenderer. I am not able to match the exact modal page animation provided by xamarin default.
protected override Task<bool> OnPushAsync(Page page, bool animated)
{
var type = page.GetType();
if (Startup.Instance.ModalPages.Contains(type))
{
var transition = CATransition.CreateAnimation();
transition.Duration = 0.5f;
transition.Type = CAAnimation.TransitionMoveIn; //tried other animation transitions
transition.Subtype = CAAnimation.TransitionFromTop;
View.Layer.AddAnimation(transition, null);
return base.OnPushAsync(page, false);
}
else
{
return base.OnPushAsync(page, animated);
}
}
protected override Task<bool> OnPopViewAsync(Page page, bool animated)
{
var type = page.GetType();
if (Startup.Instance.ModalPages.Contains(type))
{
var transition = CATransition.CreateAnimation();
transition.Duration = 0.5f;
transition.Type = CAAnimation.TransitionReveal;
transition.Subtype = CAAnimation.TransitionFromBottom;
View.Layer.AddAnimation(transition, null);
return base.OnPopViewAsync(page, false);
}
else
{
return base.OnPopViewAsync(page, animated);
}
Thanks
Is there a way to apply modal page transition animation to a normal page.
If just want to keep the same TransitionStyle with navigation of normal page. You can hide the NavigationBar when PushAsync , and not need to use PushModelAsync .
For example , Button_Clicked method of MainPage as follow :
private void Button_Clicked(object sender, EventArgs e)
{
Navigation.PushAsync(new PageTwo());
}
Then PageTwo :
public partial class PageTwo : ContentPage
{
public PageTwo()
{
InitializeComponent();
NavigationPage.SetHasNavigationBar(this, false);
}
private void Button_Clicked(object sender, EventArgs e)
{
Navigation.PopAsync();
}
}
The effect should be your want :
============================Update===================================
If need to use PushModelAsync , there is a workaround to achieve that effect .
Create a CustomPageRenderer for PageTwo in iOS solution :
[assembly: ExportRenderer(typeof(PageTwo), typeof(CustomPageRenderer))]
namespace XamarinMoelNavigationStyle.iOS
{
public class CustomPageRenderer :PageRenderer
{
public override void ViewWillAppear(bool animated)
{
var transition = CATransition.CreateAnimation();
transition.Duration = 0.5f;
transition.Type = CAAnimation.TransitionPush;
transition.Subtype = CAAnimation.TransitionFromRight;
View.Layer.AddAnimation(transition, null);
base.ViewWillAppear(animated);
}
}
}
We will add animation inside ViewWillAppear method .
When poping to previous MainPage , we can deal with that in ContentPage as follow :
private void Button_Clicked(object sender, EventArgs e)
{
PageTwoView.TranslateTo(300, 0, 500);
Device.StartTimer(TimeSpan.FromSeconds(0.5), () =>
{
// Do something
Navigation.PopModalAsync(false);
return false; // True = Repeat again, False = Stop the timer
});
}
Here PageTwoView is defined from Xaml :
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Name="PageTwoView"
x:Class="XamarinMoelNavigationStyle.PageTwo">
...
</ContentPage>
Note : When MainPage navigate to PageTwo , need to disable the animation .
Such as :
private void Button_Clicked(object sender, EventArgs e)
{
Navigation.PushModalAsync(new PageTwo(), false);
}
The effect :

How do I listen to UWP Xaml Slider manipulation start/end events?

What events should I listen to on a UWP Xaml Slider to determine when the user begins and ends manipulation.
This functionality is important when you have a slider that represents some continuously changing app state (say, an animation time) and you want to pause the update when the user interacts with the slider.
This question has been answered for WPF and Windows Phone, but not UWP. The other solutions do not work, or are incomplete, for UWP.
You need to listen to interaction events from a couple of the elements of the Slider template: the Thumb, and the Container. This is because the user can manipulate the thumb directly by clicking and dragging it, but also they can click anywhere on the slider and the thumb will jump to that location (even though it looks like you are then manipulating the Thumb, actually the thumb is just being relocated every time the mouse moves - you are still interacting with the container).
There are a couple caveats:
the thumb and container both process their input events and do not pass them on, so you need to use the AddHandler method of attaching RoutedEvent handlers so that you get events which have already been processed.
you need to attach the event handlers after the control template has been applied, which means you need to subclass the Slider to override a protected method.
The RoutedEvent handler information is covered here: https://learn.microsoft.com/en-us/windows/uwp/xaml-platform/events-and-routed-events-overview#registering-handlers-for-already-handled-routed-events
The following SliderEx class adds some events which can be used to detect when the user begins/ends interacting with the slider:
public class SliderEx : Slider
{
public event EventHandler SliderManipulationStarted;
public event EventHandler SliderManipulationCompleted;
public event EventHandler SliderManipulationMoved;
private bool IsSliderBeingManpulated
{
get
{
return this.isContainerHeld || this.isThumbHeld;
}
}
private bool isThumbHeld = false;
private bool isContainerHeld = false;
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
var thumb = base.GetTemplateChild("HorizontalThumb") as Thumb;
if (thumb == null)
{
thumb = base.GetTemplateChild("VerticalThumb") as Thumb;
}
if (thumb != null)
{
thumb.DragStarted += this.Thumb_DragStarted;
thumb.DragCompleted += this.Thumb_DragCompleted;
thumb.DragDelta += this.Thumb_DragDelta;
}
var sliderContainer = base.GetTemplateChild("SliderContainer") as Grid;
if (sliderContainer != null)
{
sliderContainer.AddHandler(PointerPressedEvent,
new PointerEventHandler(this.SliderContainer_PointerPressed), true);
sliderContainer.AddHandler(PointerReleasedEvent,
new PointerEventHandler(this.SliderContainer_PointerReleased), true);
sliderContainer.AddHandler(PointerMovedEvent,
new PointerEventHandler(this.SliderContainer_PointerMoved), true);
}
}
private void SliderContainer_PointerMoved(object sender,
Windows.UI.Xaml.Input.PointerRoutedEventArgs e)
{
this.InvokeMove();
}
private void SliderContainer_PointerReleased(object sender,
Windows.UI.Xaml.Input.PointerRoutedEventArgs e)
{
this.SetContainerHeld(false);
}
private void SliderContainer_PointerPressed(object sender,
Windows.UI.Xaml.Input.PointerRoutedEventArgs e)
{
this.SetContainerHeld(true);
}
private void Thumb_DragDelta(object sender, DragDeltaEventArgs e)
{
this.InvokeMove();
}
private void Thumb_DragCompleted(object sender, DragCompletedEventArgs e)
{
this.SetThumbHeld(false);
}
private void Thumb_DragStarted(object sender, DragStartedEventArgs e)
{
this.SetThumbHeld(true);
}
private void SetThumbHeld(bool held)
{
bool wasManipulated = this.IsSliderBeingManpulated;
this.isThumbHeld = held;
this.InvokeStateChange(wasManipulated);
}
private void SetContainerHeld(bool held)
{
bool wasManipulated = this.IsSliderBeingManpulated;
this.isContainerHeld = held;
this.InvokeStateChange(wasManipulated);
}
private void InvokeMove()
{
this.SliderManipulationMoved?.Invoke(this, EventArgs.Empty);
}
private void InvokeStateChange(bool wasBeingManipulated)
{
if (wasBeingManipulated != this.IsSliderBeingManpulated)
{
if (this.IsSliderBeingManpulated)
{
this.SliderManipulationStarted?.Invoke(this, EventArgs.Empty);
}
else
{
this.SliderManipulationCompleted?.Invoke(this, EventArgs.Empty);
}
}
}
}

TapGestureRecognizer doesn`t work on Xamarin Map

Im used similar gesture recognizer on Image and it worked. But on a
map nothing work. What could be the reason? Im testing it on Droid project.
public class MapPage : ContentPage
{
Map map;
public MapPage()
{
map = new ExtendedMap
{
IsShowingUser = true,
HeightRequest = 100,
WidthRequest = 940,
VerticalOptions = LayoutOptions.FillAndExpand
};
map.GestureRecognizers.Add(new TapGestureRecognizer
{
Command = new Command(()=> { OnAlertYesNoClicked(null, null); }),
NumberOfTapsRequired = 1
});
ContentLayout.Children.Add(map);
}
void NavClicked(object sender, EventArgs e)
{
IsShowRightPanel = !IsShowRightPanel;
}
async void OnAlertYesNoClicked(object sender, EventArgs e)
{
var answer = await DisplayAlert("Question?", "Would you like to play a game", "Yes", "No");
}
}
Not sure why it is not fired, but I can imagine it's not working because the map itself catches tap events etc.
I see you are using the ExtendedMap, have a look at TKCustomMap instead. It has a property for a Command to invoke code when the map is tapped.

How to stop led torch/flashlight app using Reflection in Windows Phone 7

I am making an Flashlight app, in which I need to use the camera's LED constantly on pressing ON button and OFF it on pressing the same button. I followed this article Turning on the LED with the video camera using Reflection. The ON/OFF operation works fine only once. The Code is as:
private VideoCamera _videoCamera;
private VideoCameraVisualizer _videoCameraVisualizer;
bool _isFlashOff = true;
private void FlashButton_Click(object sender, RoutedEventArgs e)
{
try
{
if (_isFlashOff)
{
_isFlashOff = false;
// Check to see if the camera is available on the device.
if (PhotoCamera.IsCameraTypeSupported(CameraType.Primary))
{
// Use standard camera on back of device.
_videoCamera = new VideoCamera();
// Event is fired when the video camera object has been initialized.
_videoCamera.Initialized += VideoCamera_Initialized;
// Add the photo camera to the video source
_videoCameraVisualizer = new VideoCameraVisualizer();
_videoCameraVisualizer.SetSource(_videoCamera);
}
}
else
{
_isFlashOff = true;
_videoCamera.StopRecording();
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private void VideoCamera_Initialized(object sender, EventArgs e)
{
_videoCamera.LampEnabled = true;
_videoCamera.StartRecording();
}
Since there was no implementation of StopRecording Method in VideoCamera class as specified in the article: Turning on the LED with the video camera using Reflection . I made the function as:
public void StopRecording()
{
// Invoke the stop recording method on the video camera object.
_videoCameraStopRecordingMethod.Invoke(_videoCamera, null);
}
The problem is when I again press ON button "Exception' is thrown as "TargetInvocationException". I am unable to figure out the problem that causes exception. Is StopRecording() function right..??
That's because you should initialize the camera only once. Do it during the OnNavigatedTo event, then re-use the same instances:
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
// Use standard camera on back of device.
_videoCamera = new VideoCamera();
// Event is fired when the video camera object has been initialized.
_videoCamera.Initialized += VideoCamera_Initialized;
// Add the photo camera to the video source
_videoCameraVisualizer = new VideoCameraVisualizer();
_videoCameraVisualizer.SetSource(_videoCamera);
}
private void VideoCamera_Initialized(object sender, EventArgs e)
{
isInitialized = true;
}
bool isInitialized;
bool isFlashEnabled;
private void Button_Click(object sender, System.Windows.RoutedEventArgs e)
{
if (!isInitialized)
{
MessageBox.Show("Please wait during camera initialization");
return;
}
if (!isFlashEnabled)
{
isFlashEnabled = true;
// Check to see if the camera is available on the device.
if (PhotoCamera.IsCameraTypeSupported(CameraType.Primary))
{
_videoCamera.LampEnabled = true;
_videoCamera.StartRecording();
}
}
else
{
isFlashEnabled = false;
_videoCamera.StopRecording();
}
}
Try this:
http://msdn.microsoft.com/en-us/library/hh202949.aspx

Adding events dynamically

I have n picture boxes. They should perform the following events dynamically:
private void pictureBoxMouseHover(object sender, EventArgs e)
{
if (sender is PictureBox)
{
((PictureBox)sender).BorderStyle = BorderStyle.FixedSingle;
}
}
private void pictureBoxMouseLeave(object sender, EventArgs e)
{
if (sender is PictureBox)
{
((PictureBox)sender).BorderStyle = BorderStyle.None;
}
}
private void MainMaster_Load(object sender, EventArgs e)
{
foreach (var control in Controls)
{
if (sender is PictureBox)
{
PictureBox pb=new PictureBox();
pb.Name = sender.ToString();
pb.MouseHover += new System.EventHandler(this.pictureBoxMouseHover);
pb.MouseLeave += new System.EventHandler(this.pictureBoxMouseHover);
}
}
}
I couldn't find what wrong with this; please help me.
dbaseman is right, you used wrong variable when iterating through controls.
But if you want to add this behavior to all picture boxes, then better solution is to create custom picture box, and simply place it on your form:
public class MyPictureBox : PictureBox
{
protected override void OnMouseHover(EventArgs e)
{
BorderStyle = BorderStyle.FixedSingle;
base.OnMouseHover(e);
}
protected override void OnMouseLeave(EventArgs e)
{
base.OnMouseLeave(e);
BorderStyle = BorderStyle.None;
}
}
Create this class, compile app and drag these custom picture boxes from toolbox to your form. They all will display border when mouse hover over picture box.
I think the mistake is here:
foreach (var control in Controls)
{
if (sender is PictureBox)
Sender in this case will be the window. I think you intended control.
foreach (var control in Controls)
{
if (control is PictureBox)

Resources