在Xamarin.Forms中将焦点设置到条目

22

这只是一个简化的例子,但我正在尝试设置这样一个功能,当我在我的应用程序中打开此页面时,键盘会自动弹出,准备让用户键入其对输入字段的响应。

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

如何将页面的焦点设置为第一个输入框?另外,当用户输入完第一个条目后,我该如何设置,让用户可以按下键盘上的“下一个”按钮(或类似的按钮),使应用程序带他们进入填写第二个条目的状态?

11个回答

48

使用 Focus 方法

nameentry.Focus();

如果你想让页面出现时焦点集中,你应该在OnAppearing方法中处理。

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

        nameentry.Focus();
    }

1
在代码中是否有一个特定的位置,我应该调用那个方法,以便可能改变结果? - prinsJoe
在OnAppearing()中尝试使用它,这样可以在所有控件创建和渲染完成后调用。 - Jason
抱歉我是个新手,但我该怎么做呢?我以前从未使用过OnAppearing()。那是要在页面上调用的方法吗?我该如何使用nameentry.Focus()来实现呢? - prinsJoe
Focus() 方法对我不可用,对于 EditTest 对象,我唯一可用的方法是 RequestFocus(),也许自从这个问题被提出(2015年)以来,Xamarin 库已经发生了变化? - Windgate

14
在我的一个项目中,我做了类似于这样的事情。请尝试以下示例:
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;
            }
        }
    }
}

接下来是XAML代码: XAML Code

!!! 如果我的代码有错误,请告诉我。


请注意,当您的某些控件被隐藏或禁用时。 - ADM-IT

11

在OnAppearing()方法中的最开始位置添加以下代码:

protected async override void OnAppearing()
{
    await Task.Run(async () =>
    {
        await Task.Delay(100);
        Device.BeginInvokeOnMainThread(async () =>
        {
            txtName.Focus();
        });
    });
}
注意:txtName是你的输入框的引用名称。

在Xamarin Android上对我不起作用。您还应注意正确使用TPL。您的Task.Delay(100)未被等待,因此它是“fire-and-forget”,这对于Task.Delay没有任何意义。此外,BeginInvoke...方法具有异步Action但没有人等待任何内容。 - thomasgalliker
2
@thomasgalliker 这是对我有效的解决方案。这就是为什么我发布了答案,它可能有助于其他人。我不知道你为什么要投反对票。 - Chandan Y S
7
很高兴它对你有用,但在我的情况下它并没有起作用。我特别因TPL的非常危险的使用而进行了投票(如上所述)。您是否看到这些要点:Task.Delay没有await...以及async() =>没有await。当代码真正有助于其他人时,他们会给它点赞。而这次不是我的情况。这就是这个系统。 - thomasgalliker
我通常避免编辑别人的回答,但@thomasgalliker的评论是100%正确的,并且这是一个简单的修复。通过添加async-await修复代码。(具有讽刺意味的是,答案确实在BeginInvoke内部使用了async,即使那里没有await,因此该async是无意义的。但也是无害的,所以我没有更改它。) - ToolmakerSteve
我还要提一下,让 OnAppearing 变成 async,然后 await Task.Run 是不寻常的、不必要的、麻烦的。我想不出任何好的理由这么做。总的来说,这个答案表明了回答者对于何时使用 - 以及何时不使用 - async/await 的理解不足。 - ToolmakerSteve

4

Focus()需要有一个可见的页面和可见的控件才能聚焦。

在我的情况下问题是在OnAppearing必须在页面显示 / 可见之前退出。在我的情况下有帮助的是在不同线程中等待页面可见性,然后在主(UI)线程中设置聚焦:

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

我认为在Task.Run内部使用async方法并等待Task.Delay会更好。 - pinki

3

1
谢谢。我使用了一个第三方控件,但是遇到了一些问题。 - ILIAS M. DOLAPO

1
可能是因为您的控件没有重新加载,所以可能需要使用FocusRequestFocus。您可以重写onAppearing方法,但即使您使用它,也可能由于控件未被再次设置而无法正常工作。

因此,您可以使用Xamarin社区工具包中的LifeCycleEffect。在第一次出现时,您可以使用该工具包来解决问题。

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

这里是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()
}

我建议你从这个链接上做笔记。

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


你的XAML控件在LifeCycleEffect_Loaded被调用。如果你只从那个地方调用它,那么发送者就是myEntry。而且myEntry是存在的。if测试是不必要的。方法体只需要简单地是myEntry.Focus();就可以了。(文档示例有多个元素调用相同的Loaded方法;这就是为什么它需要测试的原因。) - ToolmakerSteve

1

我知道这是一个旧的帖子,但对于某些人来说可能会起作用,就像对我一样。

protected override async void OnAppearing()
{
    base.OnAppearing();
    await Task.Delay(500);
    await Task.Run(() =>
    {
        entryname.Focus();
    });
}

0

这个对我来说完美运行了 :D

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

0
protected override void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
    base.OnPropertyChanged(propertyName);
    if (propertyName == "Renderer")
    {
        myEntry.Focus();
    }
}

0

这个简单的方法对我不起作用:

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

我必须等待一个未指定的时间,但不想等待更长时间。此外,我也不想在OnAppearing()方法中使用延迟代码污染它,因此实现了一个带有Focus方法的辅助程序:

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
    }
}

然后使用它

protected override async void OnAppearing()
{
    base.OnAppearing();
    await ViewHelper.Focus(MyEntry);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接