将焦点放在 Xamarin.Forms MVVM (Prism) 中的 Entry 字段

Set focus on Entry field in Xamarin.Forms MVVM (Prism)

我正在使用 Xamarin.Forms 和 Prism 来创建我的移动应用程序。

我有一个包含 2 个条目的屏幕。进入屏幕时,我想将焦点设置在第一个条目上。 然后在用户在此条目中输入数据并验证后,我想将焦点设置到第二个条目。

基于第一个答案: 我应该做错事。我创建了一个新的 Prism 小项目来测试它:

MainPage.xaml

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:testEntry"
             x:Class="testEntry.Views.MainPage"
             Title="{Binding Title}">

    <StackLayout HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand">
        <Label Text="Welcome to Xamarin Forms and Prism!" />

        <local:MyEntry Placeholder="" x:Name="entry1" />

        <Button Text="set focus on entry1" Clicked="Button_Clicked"/>
    </StackLayout>

</ContentPage>

MainPage.xaml.cs

using Xamarin.Forms;

namespace testEntry.Views
{
    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();
        }

        protected override void OnAppearing()
        {
            base.OnAppearing();
            entry1.Focus(); //Not Working
        }

        private void Button_Clicked(object sender, EventArgs e)
        {
            entry1.Focus(); //Working
        }
    }
}

MyEntry.cs(在主项目中)

using Xamarin.Forms;

namespace testEntry
{
    public class MyEntry : Entry
    {
    }
}

MyEntryRenderer.cs(在 Android 项目中)

using Android.Content;
using Android.Views;
using Android.Views.Accessibility;
using Xamarin.Forms.Platform.Android;

namespace testEntry.Droid
{

    public class MyEntryRenderer : EntryRenderer
    {
        public MyEntryRenderer(Context context) : base(context)
        {
        }

        public static void Focus(View view)
        {
            view.SendAccessibilityEvent(EventTypes.ViewFocused);
        }
    }
}

不幸的是,仍然没有关注我的领域:'(

有一种方法可以使用每个平台的辅助功能 API 来执行此操作。 Xamarin 表单尚不具备可访问性的所有平台功能,因此您必须创建自定义呈现器,然后在页面的生命周期事件中调用焦点方法。

因此调用此 Focus 函数会使应用程序聚焦于该元素。您通常不想这样做,因为该应用程序有目的地专注于它所做的事情,以便可访问的用户获得一致的体验。但是如果你真的想覆盖默认行为,在 Android 中是这样的

public static void Focus(View view)
        {
view.SendAccessibilityEvent(EventTypes.ViewFocused);
        }

并且在 iOS 中,您必须使用 PostNotification api,这将是此

的一些变体
UIAccessibility.PostNotification(UIAccessibilityPostNotification.ScreenChanged, entry element)

您可以进一步查看 Accessibility Focus 以获得确切答案

最后,感谢 Saamer,我找到了另一种方法,即使用 EventAggregator。

public class FocusChanged : PubSubEvent<String> { }

然后在我的视图模型中:

IEventAggregator _ea;

public MainPageViewModel(INavigationService navigationService, IEventAggregator eventAggregator) : base(navigationService)
{
    _ea = eventAggregator;
}

在 viewModel 中,每当我想将焦点设置到某个字段时,我都会发送一个事件:

_ea.GetEvent<FocusChanged>().Publish("Source");

在我的视图后面的代码中,我处理了这个事件:

IEventAggregator _ea;

 public MainPage(IEventAggregator eventAggregator)
{
    InitializeComponent();
    _ea = eventAggregator;
    _ea.GetEvent<FocusChanged>().Subscribe(SetFocusOnControl); //Name of method which will handle this event
}

/// set the focus on entry based on parameter
/// each event value will set focus on a specific entry (example: source is linked to entry txtScanSrc)
private async void SetFocusOnControl(String fieldName)
{
    Entry l_view;

    switch(fieldName)
    {
        case "source": l_view = this.FindByName<Entry>("txtScanSrc"); break;
        case "quantity": l_view = this.FindByName<Entry>("txtQty"); break;
        case "tote": l_view = this.FindByName<Entry>("txtScanTote"); break;
        case "pallet": l_view = this.FindByName<Entry>("txtScanPalout"); break;
        case "destination": l_view = this.FindByName<Entry>("txtScanDest"); break;
        default: l_view = this.FindByName<Entry>("txtScanSrc"); break;
    }

    await WaitAndExecute(500, () => { l_view.Focus(); });
}