简体   繁体   中英

Setting the Focus to an Entry in Xamarin.Forms

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?

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();
    }

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 代码

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

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.

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.");
                });
            });
        }

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.

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();
    });
}

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://docs.microsoft.com/en-us/xamarin/community-toolkit/effects/lifecycleeffect

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).

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);
}
protected override void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
    base.OnPropertyChanged(propertyName);
    if (propertyName == "Renderer")
    {
        myEntry.Focus();
    }
}

This worked perfectly for me :D

protected override async void OnAppearing() {
        base.OnAppearing();
        while(!passwordInput.Focus()) { await Task.Delay(50); }
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM