1

I'm using Xamarin.Forms and Prism to create my mobile app.

I have a screen with 2 entries. When entering the screen, I'd like to set the focus on the first entry. Then after the user entered data in this entry and validated it, I'd like to set the focus to the second entry.

Based on first answer: I should do something wrong. I've created a small new Prism project to test it :

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 (in Main project)

using Xamarin.Forms;

namespace testEntry
{
    public class MyEntry : Entry
    {
    }
}

MyEntryRenderer.cs (in Android Project)

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

Unfortunately, still nofocus on my field :'(

YoZ
  • 23
  • 1
  • 7

2 Answers2

1

Finally, and thanks to Saamer, I found another way of doing it by using EventAggregator.

public class FocusChanged : PubSubEvent<String> { }

Then in my view model :

IEventAggregator _ea;

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

In the viewModel, whenever I want to set the focus to a field, I'm sending an event :

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

And in my view's code behind, I handle this event:

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(); });
}
YoZ
  • 23
  • 1
  • 7
0

There's a way of doing this using the Accessibility APIs of each of the platforms. Xamarin forms doesn't have all the platform features of accessibility yet so you d have to create a custom renderer and then call the focus method in a life cycle event of the page.

So calling this Focus function would cause the app to focus on that element. You generally don't want to do it because the app purposefully focuses on what it does so accessible users have a consistent experience. But if you really want to override the default behavior, in Android it's something like this

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

And in iOS, you have to use the PostNotification apis which will be some variation of this

UIAccessibility.PostNotification(UIAccessibilityPostNotification.ScreenChanged, entry element)

You can look more into Accessibility Focus to get the exact answer

Saamer
  • 4,687
  • 1
  • 13
  • 55
  • I've edited my previous post with an attempt I did. Unfortunately not working. I should have done something wrong! If I'm adding a button on the page and call the entry1.focus(), it's working. It seems my issue is to get the focus on this field when screen displays. (I also tried to override OnAppearing()) – YoZ Jun 07 '19 at 12:36
  • Yay youre almost there. Try to override the OnResume life cycle event instead because we want to set the focus after the page has loaded – Saamer Jun 07 '19 at 13:08
  • I don't have any OnResume event in my view that can be overriden. The only OnResume I can see is in the App.xaml.cs but this won't apply. I don't know what to do sorry. – YoZ Jun 07 '19 at 14:08
  • Ah my bad, I got confused since i mainly work with xamarin native now. There's two ways of solving this, a hacky method, so you add this call just before the end of your OnAppearing method: await WaitAndExecute(1000, () => { // refresh code; } private async Task WaitAndExecute(int milisec, Action actionToExecute) { await Task.Delay(milisec); actionToExecute(); } – Saamer Jun 07 '19 at 15:24
  • 1
    This last solution is working to set the focus at screen appearing. Thanks a lot. – YoZ Jun 11 '19 at 08:14