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).
Related
I am trying to make a custom renderer for an editor that changes the "return" key to a "done" button and fires the Completed event when you tap it instead of typing a newline. The code in OnElementChanged() is being hit, but it's not doing anything. The "return" key is still a "return" key and it still types newlines instead of making the editor go out of focus. What am I doing wrong?
Here is the class for the custom editor (in the .NET project):
using Xamarin.Forms;
namespace Partylist.Custom_Controls
{
public class ChecklistEditor : Editor
{
}
}
Here is the custom renderer for Android:
using Android.Content;
using Android.Runtime;
using Android.Views;
using Android.Views.InputMethods;
using Android.Widget;
using Partylist.Custom_Controls;
using Partylist.Droid.Custom_Renderers;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(ChecklistEditor), typeof(ChecklistEditorRenderer))]
namespace Partylist.Droid.Custom_Renderers
{
class ChecklistEditorRenderer : EditorRenderer
{
// Constructor because it needs to exist.
public ChecklistEditorRenderer(Context context) : base(context)
{
}
// This gets overridden so I can change what I want to change.
protected override void OnElementChanged(ElementChangedEventArgs
<Editor> e)
{
// Make it do what is should normally do so it will exist.
base.OnElementChanged(e);
// Make the "Return" button on the keyboard be a "Done" button.
Control.ImeOptions = ImeAction.Done;
// Make the thing watch for when the user hits the "Return" button.
Control.EditorAction += OnEditorAction;
}
// This makes the "Return" button fire the "Completed" event
// instead of typing a newline.
private void OnEditorAction(object sender, TextView
.EditorActionEventArgs e)
{
e.Handled = false;
if (e.ActionId == ImeAction.Done)
{
Control.ClearFocus();
e.Handled = true;
}
}
}
}
Here is the custom renderer for iOS:
using Foundation;
using Partylist.Custom_Controls;
using Partylist.iOS.Custom_Renderers;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly: ExportRenderer(typeof(ChecklistEditor), typeof(ChecklistEditorRenderer))]
namespace Partylist.iOS.Custom_Renderers
{
class ChecklistEditorRenderer : EditorRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs
<Editor> e)
{
base.OnElementChanged(e);
Control.ReturnKeyType = UIReturnKeyType.Done;
}
protected override bool ShouldChangeText(UITextView textView,
NSRange range, string text)
{
if (text == "\n")
{
textView.ResignFirstResponder();
return false;
}
return true;
}
}
}
The code-behind for the page where I'm using these custom renderers (there's nothing in the XAML that should conflict with it, I think, but I'll add it to the post if people want to make sure):
using Partylist.Custom_Controls;
using System;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace Partylist.Views
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class ChecklistPage : ContentPage
{
// Struct for items on the checklist.
struct Item
{
public ChecklistEditor ItemEditor { get; set; }
public CheckBox ItemCheckbox { get; set; }
}
// Create a list of contact structs to populate the ListView.
ObservableCollection<Item> items;
// Flag for when an item is added to the list.
bool itemAdded = false;
// Constructor.
public ChecklistPage()
{
// Whatever setup stuff it was going to do anyway.
InitializeComponent();
// Set the label's BindingContext to the
// App class so it can update its text.
tipLabel.BindingContext = (App)App.Current;
}
// Override for OnAppearing().
protected override void OnAppearing()
{
// Makes the page appear.
base.OnAppearing();
// Set the page's title to be the name of the selected list.
Title = App.selectedList.Name;
// Make a toolbar item appear to access the Main Checklist
// unless we are already there.
if (App.selectedList.ListFile.Name.EndsWith(".mchec"))
{
ToolbarItems.Remove(MainChecklistButton);
}
// Set the binding context of the page to itself.
BindingContext = this;
// Start the timer for the tips banner if it is stopped.
App.tipTimer.Start();
// Set the banner's text to the current tip's sumamry.
tipLabel.Text = ((App)App.Current).CurrentTip.Summary;
OnPropertyChanged("CurrentTip");
// Subscribe the OnTipUpdate function to the tipUpdate event in the app
// class.
App.TipUpdate += OnTipUpdate;
// Make the ObservableCOllection reference something.
items = new ObservableCollection<Item>();
// Open a stream to the list that we want to display.
using (StreamReader listReader = new StreamReader(App.selectedList
.ListFile.FullName))
{
// Loop through the file and read data into the list.
while (!listReader.EndOfStream)
{
// Create a blank item.
Item newItem = new Item()
{
ItemEditor = new ChecklistEditor()
{
Text = listReader.ReadLine(),
Placeholder = "New Item",
IsTabStop = true,
AutoSize = EditorAutoSizeOption.TextChanges,
WidthRequest = 310
},
ItemCheckbox = new CheckBox()
{
Color = App.selectedList.ListItemColor,
IsChecked = bool.Parse(listReader.ReadLine())
}
};
// Subscribe OnCompleted() to the new item's "Completed"
// event.
newItem.ItemEditor.Completed += OnCompleted;
// Subscribe OnTextChanged() to the new item's
// "TextChanged" event.
newItem.ItemEditor.TextChanged += OnTextChanged;
// Add the new item to the list.
items.Add(newItem);
// Make the ListView update.
ChecklistView.ItemsSource = items;
OnPropertyChanged("contacts");
}
// Once everything is loaded, close the file.
listReader.Close();
}
}
// Override for OnDisappearing().
protected override void OnDisappearing()
{
// Makes the page disappear.
base.OnDisappearing();
// Open a stream to the file for the list.
StreamWriter listWriter = new StreamWriter(App.selectedList
.ListFile.FullName);
// Loop through the contacts list to write the contacts to the
// file.
for (int i = 0; i < items.Count; i++)
{
// Write each item to the file.
listWriter.WriteLine(items.ElementAt(i).ItemEditor.Text);
listWriter.WriteLine(items.ElementAt(i).ItemCheckbox.IsChecked);
}
// Close the stream.
listWriter.Close();
}
// Function for when the "Add New Contact" button is clicked.
private void OnAddNewItemClicked(object sender, EventArgs e)
{
// Create a blank item.
Item newItem = new Item()
{
ItemEditor = new ChecklistEditor()
{
Placeholder = "New Item",
IsTabStop = true,
AutoSize = EditorAutoSizeOption.TextChanges,
WidthRequest = 310
},
ItemCheckbox = new CheckBox()
{
Color = App.selectedList.ListItemColor,
IsChecked = false
}
};
// Subscribe OnCompleted() to the new item's "Completed"
// event.
newItem.ItemEditor.Completed += OnCompleted;
// Subscribe OnTextChanged() to the new item's
// "TextChanged" event.
newItem.ItemEditor.TextChanged += OnTextChanged;
// Add the new contact to the list.
items.Add(newItem);
// Set the "itemAdded" flag to true.
itemAdded = true;
// Make the ListView update.
ChecklistView.ItemsSource = items;
OnPropertyChanged("contacts");
// Select the new item.
ChecklistView.SelectedItem = items.ElementAt(items.Count - 1);
}
// Function for when an item is selected, used to set the focus to
// a newly added item in the list.
private async void OnItemSelected(object sender, SelectedItemChangedEventArgs e)
{
// Only runs this if an item was added (as opposed to being
// read in from the file).
if (itemAdded)
{
if (e.SelectedItem == null) return;
await Task.Delay(100); // Change the delay time if Focus() doesn't work.
((Item)e.SelectedItem).ItemEditor.Focus();
ChecklistView.SelectedItem = null;
itemAdded = false;
}
}
// Function for when the user presses "Return" on the keyboard in
// an editor.
private void OnCompleted(object sender, EventArgs e)
{
// We just want to unfocus the editor.
((Editor)sender).Unfocus();
}
// Function for when the user types anything in the editor, used
// to make sure it resizes.
private void OnTextChanged(object sender, TextChangedEventArgs e)
{
// Makes the cell resize. The cell is the parent of the
// StackLayout which is the parent of the ContentView which is
// the parent of the Editor that fired the event.
((ViewCell)((Editor)sender).Parent.Parent.Parent)
.ForceUpdateSize();
}
}
}
In Android , you need to set Single Line for EditTextView , then it will works .
For example :
...
protected override void OnElementChanged(ElementChangedEventArgs<Editor> e)
{
base.OnElementChanged(e);
// set single line will works
Control.SetSingleLine();
Control.ImeOptions = ImeAction.Done;
Control.EditorAction += OnEditorAction;
}
private void OnEditorAction(object sender, TextView.EditorActionEventArgs e)
{
e.Handled = false;
if (e.ActionId == ImeAction.Done)
{
Control.ClearFocus();
e.Handled = true;
InputMethodManager imm = (InputMethodManager)Control.Context.GetSystemService(Context.InputMethodService);
imm.HideSoftInputFromWindow(Control.WindowToken, 0);
}
}
...
The effect :
About iOS to achieve that , you can refer to follow code :
[assembly: ExportRenderer(typeof(Editor), typeof(CustomEditorRenderer))]
namespace AppEntryTest.iOS
{
class CustomEditorRenderer : EditorRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Editor> e)
{
base.OnElementChanged(e);
Control.ReturnKeyType = UIReturnKeyType.Done;
}
protected override bool ShouldChangeText(UITextView textView, NSRange range, string text)
{
if (text == "\n")
{
textView.ResignFirstResponder();
return false;
}
return true;
}
}
}
The effect :
====================================Update================================
If need to wrap text in Android , you can set background for EditTextView :
Adding bg_gray_border.xml in Resources/drawable folder :
<?xml version="1.0" encoding="utf-8" ?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#ffffff" />
<stroke
android:width="1dp"
android:color="#DEDEDE" />
<corners android:radius="6dp" />
</shape>
Used in Renderer class :
...
protected override void OnElementChanged(ElementChangedEventArgs<Editor> e)
{
base.OnElementChanged(e);
Control.SetSingleLine();
Control.SetBackgroundResource(Resource.Drawable.bg_gray_border);
Control.ImeOptions = ImeAction.Done;
Control.EditorAction += OnEditorAction;
}
...
The effect :
Add wapped text in iOS ,
...
protected override void OnElementChanged(ElementChangedEventArgs<Editor> e)
{
base.OnElementChanged(e);
Control.ReturnKeyType = UIReturnKeyType.Done;
Control.Layer.BorderColor =UIColor.Gray.CGColor;
Control.Layer.BorderWidth = 1;
Control.Layer.CornerRadius = 5;
}
...
The effect :
Here is the sample project .
In a Xamarin.Forms project, I'm trying to repeatedly translate an image from a position A(x,y) to a position B(x,y) and back, from B to A. To achieve this, I read that is possible to customize behaviors.
I extend Behavior class, overriding OnAttachedTo and OnDetachingFrom. And in the OnAttachedTo method I start a Task which repeatedly does the two translations.
This is my Behavior class:
public class MoveImageBehavior : Behavior<Image>
{
private Image _Image = null;
public static readonly BindableProperty AnimatedProperty = BindableProperty.Create("Animated", typeof(bool), typeof(ImageAnimatedBehavior), defaultValue: false);
public bool Animated
{
get { return (bool)GetValue(AnimatedProperty); }
set { SetValue(AnimatedProperty, value); }
}
protected override void OnAttachedTo(Image image)
{
base.OnAttachedTo(image);
_Image = image;
Animated = true;
Task.Run(AnimateImage);
}
protected override void OnDetachingFrom(Image image)
{
base.OnDetachingFrom(image);
_Image = null;
}
private async void AnimateImage()
{
while (_Image != null && Animated)
{
await _Image.TranslateTo(100, 100, 1000);
await _Image.TranslateTo(0, 0, 1000);
}
}
}
The image in the xaml file:
<ContentView>
<Grid>
<Image x:Name="image_translating" Source="my_icon" Aspect="AspectFit">
<Image.Behaviors>
<behaviors:MoveImageBehavior Animated="{Binding ImageTranslating}" BindingContext="{Binding BindingContext, Source={x:Reference image_translating}}"/>
</Image.Behaviors>
</Image>
</Grid>
</ContentView>
The Image repeatedly translates correctly as I want, but I'm not able to stop the while routine. The property binding doesn't work when Animated is set to false in the ViewModel and OnDetachingFrom is never called.
What am I doing wrong? Any suggestions?
Through the document, we can see that:
The OnDetachingFrom method is fired when the behavior is removed from
the control. This method receives a reference to the control to which
it is attached, and is used to perform any required cleanup. For
example, you could unsubscribe from an event on a control to prevent
memory leaks.
It will only fired when you remove the behavior from the image. I would give you an example about how to stop the animation:
I defined an bool property in the code behind to control stop or not stop:
public bool showA = true;
And I add a button as an example to stop the animation:
private void Button_Clicked(object sender, EventArgs e)
{
showA = !showA;
if (showA)
{
image_translating.Behaviors.Add(new MoveImageBehavior());
}
else
{
var toRemove = image_translating.Behaviors.FirstOrDefault(b => b is MoveImageBehavior);
if (toRemove != null)
{
image_translating.Behaviors.Remove(toRemove);
}
}
}
Also in your OnDetachingFrom method, do not set the image to null, it will cause a null expection, just set the Animated to false :
protected override void OnDetachingFrom(Image image)
{
base.OnDetachingFrom(image);
Animated = false;
}
You can convert my click event to some binding in your project and make it work.
Refer: creating-a-xamarinforms-behaviorer
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);
}
}
}
}
In Xamarin Forms, when I use the following code:
public SomePage()
{
InitializeComponent();
someEntry.Focus();
}
the code entry isn't focused by default, however, if I use the following code:
protected override void OnAppearing()
{
base.OnAppearing();
someEntry.Focus();
}
it works as needed (entry is focused). Why is that? Isn't codeEntry already existing and sitting at it's place, fully functional, after InitializeComponent() call? I mean, I sure can change Text property from page constructor.
In Xamarin, every control has equivalent view renderer, that is native UI element which will only be created when control is added to the native element hierarchy. In constructor, native element for entry is not yet created. However, in OnAppering, entry's corresponding native element is created so it can get the focus.
Also this seems like a bug as Xamarin is storing state and applying it when creating the native UI element. Its time to file a bug !!!
When I use Shell this don't work anymore.
protected override void OnAppearing()
{
base.OnAppearing();
someEntry.Focus();
}
But this does work:
protected async override void OnAppearing()
{
base.OnAppearing();
await Task.Delay(100);
someEntry.Focus();
}
File.xaml
<Entry x:Name="txtLPN" Placeholder="Scan LPN." Grid.Row="1" Grid.Column="0" FontSize="15" Focused="txtLPN_Focused" />
File.cs >>>>>
private void txtLPN_Focused(object sender, FocusEventArgs e)
{
txtLPN.CursorPosition = 0;
if (!string.IsNullOrEmpty(txtLPN.Text))
txtLPN.SelectionLength = txtLPN.Text.Length;
}
protected async override void OnAppearing()
{
base.OnAppearing();
await Task.Delay(600);
txtLPN.Focus();
}
I tried all of the above. I think because my page is a pop-up and has some animation none of the above worked. However this worked for me:
BackgroundWorker setFocus = new BackgroundWorker();
In constructor
setFocus.DoWork += SetFocus_DoWork;
private void SetFocus_DoWork(object sender, DoWorkEventArgs e)
{
bool worked = false;
while (!worked)//will keep trying until it can set focus (when MyEntry is rendered)
{
Thread.Sleep(1);
MainThread.InvokeOnMainThreadAsync(()=> worked = MyEntry.Focus());
}
}
protected override void OnAppearing()
{
base.OnAppearing();
if(!setFocus.IsBusy)
{
setFocus.RunWorkerAsync();
}
}
You may want to add something to handle if "worked" is never set to true like try this for a few seconds.
You can use the Xamarin Community Toolkit LifecycleEffect to call some code when the renderer for the Entry is initialized/cleaned up. Combine this with OnAppearing to reliably show the keyboard without using cheap DoEvents hacks like await Task.Yield() or await Task.Delay(100).
XAML:
<Entry x:Name="userName">
<Entry.Effects>
<xct:LifecycleEffect Loaded="LifecycleEffect_Loaded" Unloaded="LifecycleEffect_Unloaded" />
</Entry.Effects>
</Entry>
C#:
private bool userNameLoaded = false;
protected override void OnAppearing()
{
base.OnAppearing();
if (userNameLoaded)
{
userName.Focus();
}
}
private void LifecycleEffect_Loaded(object sender, System.EventArgs e)
{
if (sender == userName)
{
userNameLoaded = true;
userName.Focus();
}
}
private void LifecycleEffect_Unloaded(object sender, System.EventArgs e)
{
if (sender == userName)
{
userNameLoaded = false;
}
}
OnAppearing won't be called after background + resume on iOS, so you'll need to hook into Application.OnResume to show focus if the user backgrounds + restores the app on iOS:
protected override void OnResume()
{
if (Xamarin.Forms.Device.RuntimePlatform == Xamarin.Forms.Device.iOS)
{
// TODO: Use Messenger, check Shell.Current.CurrentPage, etc. to set focus.
}
}
underneath of Xamarin form it is android activity or iOS 's UIviewController's page life cycle works. someEntry.Focus(); will not work in your constructor
In a SL4 application i need to restyle my TabItems (actually add a button in the header).
So i took the TabItem's control template from here and added the functionality i wanted.
This seems to work fine, (i could dynamically add tabitems) with one exception:
i think this posted control template is behaving somehow "arbitrary": every time the mouse hoovers over a non selected TabItem header, this gets selected WHITHOUT clicking!! (afaik this is not the default behavior: the user user has to click a header to make this tabitem the selected one).
I tried to find why it is behaving like this, with no luck!
Is there someone who can enlighten my darkness???
Thanks in advance!
Well it turns out the error was not in the control template but in the class, the style was applied to.
In detail: the class the style was applied to is the following (in it you will see my comment about the "wrong behavior"):
public class WorkspaceViewModel : TabItem
{
public WorkspaceViewModel()
{
DefaultStyleKey = typeof(WorkspaceViewModel);
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
Button closeButtonSel = base.GetTemplateChild("PART_CloseTopSelected") as Button;
Button closeButtonUnsel = base.GetTemplateChild("PART_CloseTopUnSelected") as Button;
if (closeButtonSel != null)
closeButtonSel.Click += new RoutedEventHandler(closeButtonSel_Click);
if (closeButtonUnsel != null)
closeButtonUnsel.Click += new RoutedEventHandler(closeButtonSel_Click);
//this part is causing the effect i was complaining about!
//and has to be removed
this.MouseEnter += delegate(object sender, MouseEventArgs e)
{
IsSelected = true;
};
}
void closeButtonSel_Click(object sender, RoutedEventArgs e)
{
//this is the close request method used in the CloseTabItemCommand
OnRequestClose();
}
#region CloseTabItemCommand
private RelayCommand closeTabItemCommand;
public ICommand CloseTabItemCommand
{
get
{
if (this.closeTabItemCommand == null)
this.closeTabItemCommand = new RelayCommand(p => this.OnRequestClose(), p => this.CanCloseTabItem());
return this.closeTabItemCommand;
}
}
private bool CanCloseTabItem()
{
return true;
}
public event EventHandler RequestClose;
private void OnRequestClose()
{
if (RequestClose != null)
RequestClose(this, EventArgs.Empty);
}
#endregion
}