Could you please let me know how can I recognize long press gesture in Xamarin Forms application?
A few days before I used TapGestureRecognizer
TapGestureRecognizer imageTap = new TapGestureRecognizer();
imageTap.Tapped += (sender, args) => this.OnClickImage;
image.GestureRecognizers.Add(imageTap);
But I don't know how to make long press gesture according to this thread from xamarin forum
It should looks something like this, but it does not work.
var dumpParam = new RelayGesture((g, x) => DisplayAlert("Title", "Hello message", "Cancel"));
book.Cover.SetValue(Gestures.InterestsProperty, new GestureCollection() {
new GestureInterest
{
GestureType = GestureType.LongPress
GestureCommand = // what should I set?
GestureParameter = dumpParam
}
});
How to set my custom handler method?
You can do it cross platform way by attaching the below behavior, as long as it is Xamarin.Forms.Button or a sub-type of it.
using System;
using System.Threading;
using System.Windows.Input;
using Xamarin.Forms;
namespace App.Controls.Behaviors
{
public class LongPressBehavior : Behavior<Button>
{
private readonly object _syncObject = new object();
private const int Duration = 1000;
//timer to track long press
private Timer _timer;
//the timeout value for long press
private readonly int _duration;
//whether the button was released after press
private volatile bool _isReleased;
/// <summary>
/// Occurs when the associated button is long pressed.
/// </summary>
public event EventHandler LongPressed;
public static readonly BindableProperty CommandProperty = BindableProperty.Create(nameof(Command),
typeof(ICommand), typeof(LongPressBehavior), default(ICommand));
public static readonly BindableProperty CommandParameterProperty =
BindableProperty.Create(nameof(CommandParameter), typeof(object), typeof(LongPressBehavior));
/// <summary>
/// Gets or sets the command parameter.
/// </summary>
public object CommandParameter
{
get => GetValue(CommandParameterProperty);
set => SetValue(CommandParameterProperty, value);
}
/// <summary>
/// Gets or sets the command.
/// </summary>
public ICommand Command
{
get => (ICommand)GetValue(CommandProperty);
set => SetValue(CommandProperty, value);
}
protected override void OnAttachedTo(Button button)
{
base.OnAttachedTo(button);
this.BindingContext = button.BindingContext;
button.Pressed += Button_Pressed;
button.Released += Button_Released;
}
protected override void OnDetachingFrom(Button button)
{
base.OnDetachingFrom(button);
this.BindingContext = null;
button.Pressed -= Button_Pressed;
button.Released -= Button_Released;
}
/// <summary>
/// DeInitializes and disposes the timer.
/// </summary>
private void DeInitializeTimer()
{
lock (_syncObject)
{
if (_timer == null)
{
return;
}
_timer.Change(Timeout.Infinite, Timeout.Infinite);
_timer.Dispose();
_timer = null;
Debug.WriteLine("Timer disposed...");
}
}
/// <summary>
/// Initializes the timer.
/// </summary>
private void InitializeTimer()
{
lock (_syncObject)
{
_timer = new Timer(Timer_Elapsed, null, _duration, Timeout.Infinite);
}
}
private void Button_Pressed(object sender, EventArgs e)
{
_isReleased = false;
InitializeTimer();
}
private void Button_Released(object sender, EventArgs e)
{
_isReleased = true;
DeInitializeTimer();
}
protected virtual void OnLongPressed()
{
var handler = LongPressed;
handler?.Invoke(this, EventArgs.Empty);
if (Command != null && Command.CanExecute(CommandParameter))
{
Command.Execute(CommandParameter);
}
}
public LongPressBehavior()
{
_isReleased = true;
_duration = Duration;
}
public LongPressBehavior(int duration) : this()
{
_duration = duration;
}
private void Timer_Elapsed(object state)
{
DeInitializeTimer();
if (_isReleased)
{
return;
}
Device.BeginInvokeOnMainThread(OnLongPressed);
}
}
}
In the XAML UI:
<Button x:Name="MyButton" Text="Long Press Me!">
<Button.Behaviors>
<behaviors:LongPressBehavior LongPressed="MyButton_LongPressed"/>
</Button.Behaviors>
</Button>
XAML UI with Command Binding:
<Button x:Name="MyButton" Text="Long Press Me!">
<Button.Behaviors>
<behaviors:LongPressBehavior Command="{Binding CommandInViewModel}"/>
</Button.Behaviors>
</Button>
Make use of XLabs.Forms nuget package, which make long press and other gesture in PCL code only.
Use of XLabs.Forms package will reduce the need of custom rendering in individual platforms...
Add XAML code in .xaml file and attached event handler in .xaml.cs file..
It is working fine in Android..
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MultiImage.Page1"
xmlns:lc="clr-namespace:XLabs.Forms.Controls;assembly=XLabs.Forms"
xmlns:lb="clr-namespace:XLabs.Forms.Behaviors;assembly=XLabs.Forms">
<ContentPage.Content>
<lc:GesturesContentView ExcludeChildren="False" GestureRecognized="GesturesContentView_GestureRecognized">
<lb:Gestures.Interests>
<lb:GestureCollection>
<lb:GestureInterest GestureType="SingleTap"/>
<lb:GestureInterest GestureType="LongPress"/>
<lb:GestureInterest GestureType="DoubleTap"/>
</lb:GestureCollection>
</lb:Gestures.Interests>
<Image Source="Myimage.png" Aspect="AspectFit" HeightRequest="100"/>
</lc:GesturesContentView>
</ContentPage.Content>
C# backend code:
private void GesturesContentView_GestureRecognized(object sender, GestureResult e)
{
switch (e.GestureType)
{
case GestureType.LongPress:
//Add code here
break;
case GestureType.SingleTap:
// Add code here
break;
case GestureType.DoubleTap:
// Add code here
break;
default:
break;
}
I recently came across this problem and found a useful post on the topic https://alexdunn.org/2017/12/27/xamarin-tip-xamarin-forms-long-press-effect/
This makes use of the RoutingEffect and goes through an example of how to create both iOS and Android implementation. The simplicity of this allows you to attach it to any view in your app without recreating code.
Surfing the internet I found the solution. There are few steps which you should reproduce.
1) Inherit the control you need the gestures on (i.e. if you want to add gesture to Xamarin.Forms.Image, create you own ImageWithLongPressGesture class).
public class ImageWithLongPressGesture : Xamarin.Forms.Image
{
public EventHandler LongPressActivated;
public void HandleLongPress(object sender, EventArgs e)
{
//Handle LongPressActivated Event
}
}
2) Expose public events for the needed gestures.
3) Create a Renderer for each platform.
4) In the Renderer, handle the gestures and bubble them to your control.
[assembly: ExportRenderer(typeof(ImageWithLongPressGesture), typeof(LongPressGestureRecognizerImageRenderer))]
namespace App1.Droid.DroidRenderers
{
public class LongPressGestureRecognizerImageRenderer : ImageRenderer
{
ImageWithLongPressGesture view;
public LongPressGestureRecognizerImageRenderer()
{
this.LongClick += (sender, args) => {
Toast.MakeText(this.Context, "Long press is activated.", ToastLength.Short).Show();
};
}
protected override void OnElementChanged(ElementChangedEventArgs<Image> e)
{
base.OnElementChanged(e);
if(e.NewElement != null)
{
view = e.NewElement as ImageWithLongPressGesture;
}
}
}
}
This solution is a hybrid of answer on xamarin forms forum and Touch and Gestures presentation by Telerik.
//To Add Programatically:
StackLayout _Containter = new StackLayout();
StackLayout _StackLayout = new StackLayout();
_StackLayout.Children.Add(new Label(){Text="Execute Me"});
GesturesContentView Gv = new GesturesContentView();
_StackLayout.SetValue(XLabs.Forms.Behaviors.Gestures.InterestsProperty, new GestureCollection() {
new GestureInterest() { GestureType = GestureType.SingleTap },
new GestureInterest() { GestureType = GestureType.LongPress },
new GestureInterest() { GestureType = GestureType.DoubleTap }
});
Gv.GestureRecognized += Gv_GestureRecognized;
Gv.ExcludeChildren = false;
Gv.Content = _StackLayout;
_Containter.Children.Add(Gv);
In order to get this to work properly on iOS, you need to use XLabs.Forms.XFormsAppiOS.Init(); in your AppDelegate.cs file just before the LoadApplication(new App()); statement.
The posted code from #zafar works if you register BindingContextChanged event.
(My post is only an add, to the original post from #zafar.)
Problem was:
if using CommandParameter="{Binding .}" resulting Parameter was always null.
You need to Register BindingContextChanged event in the OnAttachedTo function.
[...]
protected override void OnAttachedTo(Button button)
{
base.OnAttachedTo(button);
this.BindingContext = button.BindingContext;
button.BindingContextChanged += handleBindingContextChanged; //this was missing
button.Pressed += Button_Pressed;
button.Released += Button_Released;
}
private void handleBindingContextChanged(object sender, EventArgs e)
{
this.BindingContext = ((Button)sender).BindingContext;
}
protected override void OnDetachingFrom(Button button)
{
base.OnDetachingFrom(button);
this.BindingContext = null;
button.Pressed -= Button_Pressed;
button.Released -= Button_Released;
button.BindingContextChanged -= handleBindingContextChanged; //also don't forget this
}
[...]
sry for the errors, this is my first post (not enough Reputation for commenting).
I am using Unity 5.2 UI. I am working on a game for iOS. I have a custom keyboard. I want to add the functionality to the del/backspace key so that when i hold the del key for more than 2 secs, it deletes the whole word instead of a single letter, which it deletes on single clicks. How do I achieve that?
Using the UGUI event you'd create a script like the following and attach it to your button:
using UnityEngine;
using UnityEngine.EventSystems;
public class LongPress : MonoBehaviour, IPointerDownHandler, IPointerUpHandler {
private bool isDown;
private float downTime;
public void OnPointerDown(PointerEventData eventData) {
this.isDown = true;
this.downTime = Time.realtimeSinceStartup;
}
public void OnPointerUp(PointerEventData eventData) {
this.isDown = false;
}
void Update() {
if (!this.isDown) return;
if (Time.realtimeSinceStartup - this.downTime > 2f) {
print("Handle Long Tap");
this.isDown = false;
}
}
}
I would like to capture all events within a GWT frame. I've found several ways to do this, but they only return mousemove and mouseout events. I also need keypresses, input, etc. The goal is to capture the events and send them to another client by using websockets, and then replicate them on the other side (co-browsing).
I am using a page on the same domain within the frame.
public class ESinkFrame extends Frame implements EventListener {
public ESinkFrame(String src){
super(src);
DOM.sinkEvents(getElement(), Event.KEYEVENTS);
DOM.sinkEvents(getElement(), Event.MOUSEEVENTS);
}
public void onBrowserEvent(Event event) {
System.out.println( "sunk event: " + DOM.eventGetTypeString(event) );
}
}
And when I use it, I also try to attach a different way of grabbing the events.
ESinkFrame frame = new ESinkFrame("http://127.0.0.1:8888/other.html");
RootPanel.get().add(frame);
FrameElement frameElt = frame.getElement().cast();
Document frameDoc = frameElt.getContentDocument();
BodyElement body = frameDoc.getBody();
Element el = body.cast();
DOM.setEventListener(el, new EventListener()
{
public void onBrowserEvent(Event event)
{
Window.alert("test");
}
});
DOM.sinkEvents(el, Event.KEYEVENTS);
Event.addNativePreviewHandler(new NativePreviewHandler(){
public void onPreviewNativeEvent(NativePreviewEvent event) {
String eventName = event.getNativeEvent().getType();
if (event.isFirstHandler() /* && (event.getTypeInt() & Event.MOUSEEVENTS) == 0*/)
System.out.println("PreviewHandler: " + eventName);
}
});
I want to catch the touch event in OS5. I use this method protected boolean touchEvent(TouchEvent message) in ListFieldRich. But this method didn't run it. I press all keys nothing happens. Even if I debug my code, this method didn't run when press keys.
How can I know the touch event in OS5?
Thanks
Touchevent will be called if you have a touch screen phone, and you touch the screen. It will not be called if you press the keys or buttons. Did you touch the screen?
I resolved my problem. I used this method protected boolean trackwheelClick(int status, int time)
setChangeListener() on your RichTextField and override these method in your Screen class
protected boolean navigationClick(int status, int time) {
if(Touchscreen.isSupported()) {
return false;
}
fieldChangeNotify(1);
return true;
}
//for touch
protected boolean touchEvent(TouchEvent message) {
if (TouchEvent.CLICK == message.getEvent()) {
FieldChangeListener listener = getChangeListener();
if (null != listener)
listener.fieldChanged(this, 1);
}
return super.touchEvent(message);
}
and in fieldChanged() method
public void fieldChanged(Field field, int context) {
if (field == yourRichTextField) {
Dialog.inform("RichtextField Clicked Button Pressed");
}
In my windows phone app, I need to track some events to get a good flow. But I'm not sure how to handle them in good sequence.
What needs to be done at startup of the app:
Main view is loaded and corresponding view model instantiated
In the constructor of the view model I initiate a login sequence that signals when completed with an eventhandler
Now when the login sequence has finished AND the view is completely loaded I need to startup another sequence.
But here is the problem, the order of these 2 events 'completing' is not always the same...
I've use the EventToCommand from MVVMLight to signal the view model that the view has 'loaded'.
Any thoughts on how to synchronize this.
As you should not use wait handles or something similar on the UI thread. You will have to sync the two method using flags in your view model and check them before progressing.
So, implement two boolean properties in your view model. Now when the login dialog is finished set one of the properties (lets call it IsLoggedIn) to true, and when the initialization sequence is finished you set the other property (how about IsInitialized) to true. The trick now lies in the implementation of the setter of these two properties:
#region [IsInitialized]
public const string IsInitializedPropertyName = "IsInitialized";
private bool _isInitialized = false;
public bool IsInitialized {
get {
return _isInitialized;
}
set {
if (_isInitialized == value)
return;
var oldValue = _isInitialized;
_isInitialized = value;
RaisePropertyChanged(IsInitializedPropertyName);
InitializationComplete();
}
}
#endregion
#region [IsLoggedIn]
public const string IsLoggedInPropertyName = "IsLoggedIn";
private bool _isLoggedIn = false;
public bool IsLoggedIn {
get {
return _isLoggedIn;
}
set {
if (_isLoggedIn == value)
return;
var oldValue = _isLoggedIn;
_isLoggedIn = value;
RaisePropertyChanged(IsLoggedInPropertyName);
InitializationComplete();
}
}
#endregion
public void InitializationComplete() {
if (!(this.IsInitialized && this.IsLoggedIn))
return;
// put your code here
}
Alternatively you can remove the InitializationComplete from the setters and change InitializationComplete to:
public void InitializationComplete() {
// put your code here
}
Then subscribe to the 'PropertyChanged' event use the following implementation:
private void Class1_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) {
if (e.PropertyName == IsInitializedPropertyName || e.PropertyName == IsLoggedInPropertyName) {
if (this.IsInitialized && this.IsLoggedIn)
InitializationComplete();
}
}