Setting the Focus to an Entry in Xamarin.Forms
Asked Answered
H

11

22

This is just a simplified example, but I'm trying to set this up so that when I open up this page in my Application, the first thing that happens is the keyboard pops up ready for the user to type in their response to an Entry field.

    var namelabel = new Label { Text = "What is your name?" };
    var nameentry = new Entry { Placeholder = "Type here..." };
    var colorlabel = new Label { Text = "What's your fav color?" };
    var colorentry = new Entry { Placeholder = "Type here..." };
    Content = new StackLayout {
       Spacing = 15,
       Children = { namelabel, nameentry, colorlabel, colorentry }
    };

How can I set the focus of the page to the first entry? And additionally, after the user has put in their first entry, how could I set this up so that the user could press a "Next" Button on the Keyboard (or something along those lines) and the app will take them to fill in the second entry?

Haugh answered 1/7, 2015 at 18:32 Comment(0)
B
48

Use the Focus method

nameentry.Focus();

If you want the focus to be set when your page appears, you should probably do this in the OnAppearing method

    protected override void OnAppearing ()
    {
        base.OnAppearing ();

        nameentry.Focus();
    }
Bluebeard answered 1/7, 2015 at 19:17 Comment(4)
Is there a specific place in the code that I should call that method in that might change the result?Haugh
try it in OnAppearing() so that it is called after all of the controls are created and renderedBluebeard
Sorry for being a noob-nuisance, but how do I do that? I've never worked with OnAppearing() before. Is that a method to call on the page? How would I do that with the nameentry.Focus()?Haugh
The Focus() method is not available for me, the only available method on an EditTest object that worked for me is RequestFocus(), maybe Xamarin libraries changed since this question was made (2015)?Charlotte
L
14

In one of my projects I did something like this. Please try the following example:

public class EntryFocusBehavior : Behavior<Entry>
{
    public string NextFocusElementName { get; set; }

    protected override void OnAttachedTo(Entry bindable)
    {
        base.OnAttachedTo(bindable);
        bindable.Completed += Bindable_Completed;
    }

    protected override void OnDetachingFrom(Entry bindable)
    {
        bindable.Completed -= Bindable_Completed;
        base.OnDetachingFrom(bindable);
    }

    private void Bindable_Completed(object sender, EventArgs e)
    {
        if (string.IsNullOrEmpty(NextFocusElementName))
            return;

        var parent = ((Entry)sender).Parent;
        while (parent != null)
        {
            var nextFocusElement = parent.FindByName<Entry>(NextFocusElementName);
            if (nextFocusElement != null)
            {
                nextFocusElement.Focus();
                break;
            }
            else
            {
                parent = parent.Parent;
            }
        }
    }
}

And then XAML: XAML Code

!!! Please let me know if I made a mistake in the code.

Lenhard answered 17/3, 2019 at 21:57 Comment(1)
Blease be aware when some of your controls are being hidden or disabled.Lenhard
M
11

Just inside OnAppearing(), add the following code,

protected async override void OnAppearing()
{
    await Task.Run(async () =>
    {
        await Task.Delay(100);
        Device.BeginInvokeOnMainThread(async () =>
        {
            txtName.Focus();
        });
    });
}

Note: txtName is the reference name of your Entry Field.

Martingale answered 18/9, 2019 at 10:0 Comment(5)
Doesn't work for me on Xamarin Android (at least). You should also pay attention to correct use of TPL. Your Task.Delay(100) is not awaited, so it is "fire-and-forget" which doesn't make any sense for Task.Delay. Also, the BeginInvoke... method has an async Action but noone awaits anything.Methylal
@Methylal That's the solution worked for me. That's why I posted the answer and it might help for others. I don't know why you downvoted.Martingale
It's good that it worked in your case. It didn't work in my case. The downvote is particularly for the very dangerous use of TPL (as described above). Do you see the points: Task.Delay with no await... and the async () => with no await. When the code is really helpful to others, they gonna upvote it. It wasnt for me. That's the system.Methylal
I usually avoid editing someone's answre, but @thomasgalliker's comment is 100% correct, and its a straightforward fix. Fixing the code by adding async-await. (Ironically, answer does use async inside BeginInvoke.., even though there are no awaits there, so that async is pointless. But also harmless, so I didn't touch that.)Benham
I'll also mention that making OnAppearing be async, and then awaiting Task.Run is .. unusual, unnecessary, troublesome. I can think of no good reason to do so. In general, this answer shows a lack of understanding of when to use - and not to use - async/await.Benham
U
4

Focus() needs to have a visible page and visible control to focus on.

The issue in my case was that is is necessary that OnAppearing has to exit before a page is shown / visible. What helped in my case is to wait for page visibility in a different thread and then set the focus in the main (UI) thread:

protected override void OnAppearing()
        {
            base.OnAppearing();

            Task.Run(() =>
            {
                while (!IsVisible)
                {
                    Debug.WriteLine("Page not visible, waiting...");
                    Task.Delay(50).Wait();
                }

                Device.BeginInvokeOnMainThread(() =>
                {
                    bool gotFocus = Entry.Focus();
                    if (!gotFocus)
                        Debug.WriteLine("Could not set focus.");
                });
            });
        }
Unsuspected answered 27/1, 2021 at 9:4 Comment(1)
I think an async method inside Task.Run and awaiting Task.Delay would be better.Stew
N
3

So long as the element to focus on is the topmost element, this should work. I place this in the OnAppearing method.

base.OnAppearing();
Entry entry = this.FindByName<Entry>("NameOfEntryElement");
entry.Focus();

The source of this info is here: https://forums.xamarin.com/discussion/100354/entry-focus-not-working-for-android

There is further discussion in the article about timing issues.

Normally answered 21/6, 2018 at 19:52 Comment(1)
thanks. I used for a third party control which i am having issues with.Absinthe
C
1

I know this is an old thread but this might work for someone as it worked for me.

protected override async void OnAppearing()
{
    base.OnAppearing();
    await Task.Delay(500);
    await Task.Run(() =>
    {
        entryname.Focus();
    });
}
Correll answered 11/11, 2020 at 0:56 Comment(0)
E
1

It's possible that Focus or RequestFocus because your control didn't load again. You can override onAppearing but even if you use that, it may possible that didn't work because the control is not set again.

So you can use onAppearing, but for the fist apparing you may use Xamarin community toolkit. And LifeCycleEffect

<Entry x:Name="myEntry"> 
    <Entry.Effects> 
        <xct:LifecycleEffect Loaded="LifeCycleEffect_Loaded" /> 
    </Entry.Effects> 
</Entry>

Here in C#

void LifeCycleEffect_Loaded(object? sender, EventArgs e)
{
    if ( sender is Entry && (sender as Entry ).Name != null && (sender as Entry).Name.Equals("myEntry") )
        // myEntry.Focus() or myEntry.RequestFocus() or (sender as Entry).Focus()
}

I suggest you to take notes from this link

https://learn.microsoft.com/en-us/xamarin/community-toolkit/effects/lifecycleeffect

Eckenrode answered 20/5, 2022 at 21:13 Comment(1)
Your xaml controls where LifeCycleEffect_Loaded is called. If you only call it from that one place, then sender is myEntry. And myEntry exists. The if tests are not needed. Its safe to have the body of the method simply be myEntry.Focus();. (The doc example has multiple elements calling the same Loaded method; that is why it needs tests.)Benham
C
0

Can anyone explain to me why this doesn't work

    protected override void OnAppearing()
    {
       base.OnAppearing();
       CustomerNameEntry.Focus();       
    }

But this does (adding async and Task.Delay(1))

    protected override async void OnAppearing()
    {
         base.OnAppearing();
         await Task.Delay(1);
         CustomerNameEntry.Focus();       
    }

I'd rather not add this clutter, it seems hacky but it's the only way I've been able to get it to work (I've also tried invoking on main thread, to no avail).

Clepsydra answered 28/5, 2020 at 14:6 Comment(1)
Presumably whatever animation is being done with the navigate, the new page hasn't yet appeared before you try do the focus (OnAppearing). And the delay probably waits for the UI to catch up, and then in that case, the animation would have finished, so it works.Parquetry
S
0

The simple one did not work for me:

protected override void OnAppearing()
{
    MyEntry.Focus();
}

I had to wait an unspecified amount of time, but did not want to wait longer than necessary. Also, I didn't want to pollute OnAppearing() with delay code, so implemented a helper with a Focus methdo that works:

using System.Threading.Tasks;
using Xamarin.Forms;

namespace MyApp.Xmrn.Views.Helpers
{
    internal static class ViewHelper
    {
        // Disable the warning about a non-awaited async, as we use it as
        // fire and forget and return from the method immediately.
#pragma warning disable 1998, 4014

        /// <summary>
        /// Entry.Focus replacement, that tries to focus a control
        /// multiple times until it succeeds.
        /// </summary>
        /// <param name="entry">Entry control to be focused.</param>
        internal static async Task Focus(Entry entry)
        {
            Task.Run(async () =>
            {
                int threshold = 20;

                while (!entry.Focus() && threshold-- > 0)
                    await Task.Delay(50);
            });
        }
#pragma warning restore 1998, 4014
    }
}

And then to use it

protected override async void OnAppearing()
{
    base.OnAppearing();
    await ViewHelper.Focus(MyEntry);
}
Snowshed answered 11/12, 2021 at 13:27 Comment(0)
F
0
protected override void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
    base.OnPropertyChanged(propertyName);
    if (propertyName == "Renderer")
    {
        myEntry.Focus();
    }
}
Formulism answered 15/1, 2022 at 16:31 Comment(0)
D
0

This worked perfectly for me :D

protected override async void OnAppearing() {
        base.OnAppearing();
        while(!passwordInput.Focus()) { await Task.Delay(50); }
    }
Doting answered 17/5, 2022 at 19:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.