1
votes

In my Xamarin app, I want that when value is being entered in an Entry, it automatically finds next Entry field and set focus to it.

I can move to next entry with Next button in keyboard, but I want to set focus on next entry automatically when value is entered (max length is 1 character).

I also tried this code, but it didn't work.

.xml

xmlns:behaviors="clr-namespace:Mobile.App.Behaviors"
<Grid Grid.Column="1">
    <Frame CornerRadius="12">
        <BoxView Color="#EEEEEE" />
    </Frame>
    <Entry
        x:Name="PasswordOne"
        Text="{Binding PasswordOne}"
        IsPassword="True"
        Keyboard="Numeric"
        MaxLength="1"
        ReturnType="Next"
        Unfocused="Password_Unfocused">
        <Entry.Behaviors>
            <behaviors:FocusOnReturnBehavior FocusOn="{x:Reference PasswordTwo}" />
        </Entry.Behaviors>
    </Entry>
</Grid>

<Grid Grid.Column="2">
    <Frame CornerRadius="12">
        <BoxView Color="#EEEEEE" />
    </Frame>
    <Entry
        x:Name="PasswordTwo"
        Text="{Binding PasswordTwo}"
        IsPassword="True"
        Keyboard="Numeric"
        MaxLength="1"
        ReturnType="Next"
        Unfocused="Password_Unfocused">
        <Entry.Behaviors>
            <behaviors:FocusOnReturnBehavior FocusOn="{x:Reference PasswordThree}" />
        </Entry.Behaviors>
    </Entry>
</Grid>

FocusOnReturnBehavior.cs

public class FocusOnReturnBehavior : Behavior<Entry>
{
    public VisualElement FocusOn { get; set; }

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


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

    private void BindableOnCompleted(object sender, EventArgs args)
    {
        FocusOn?.Focus();
    }
}
2
Have you attempted to debug it? is your behavior getting attached? Is the event being assigned? Does the handler get called?Jason
I updated the behavior with @FabriBertani and debug it. OnAttachedTo() is trigger when I'm navigating from page to PasswordPage, but at a time of working with Entry in PasswordPage, Behavior didn't triggered (breakpoints)..user14139029

2 Answers

1
votes

I will change the behavior for something like:

public class FocusOnReturnBehavior : Behavior<Entry>
{
    public static readonly BindableProperty FocusOnProperty = BindableProperty.Create(
        nameof(FocusOn),
        typeof(object),
        typeof(FocusOnReturnBehavior),
        null
    );

    public object FocusOn
    {
        get => (object)GetValue(FocusOnProperty);
        set => SetValue(FocusOnProperty, value);
    }

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


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

    private void BindableOnCompleted(object sender, EventArgs args)
    {
        if (FocusOn == null)
            return;
        else if (FocusOn is Entry entry)
            entry.Focus();
        else if (FocusOn is Button button)
        {
            button.Focus();
            button.Command.Execute(null)
        }
        else if (FocusOn is VisualElement element)
            element.Focus();
    }
}
0
votes

In your question, the completed event won't triggered when you enter 1 character into the entry.

You can add you entries to a list and make next entry focus when current entry.text.length is 1.

I wrote a simple example to achieve that:

<StackLayout>
    <!-- Place new controls here -->
    <Entry  MaxLength="1" TextChanged="Entry_TextChanged"
       HorizontalOptions="Center"
       VerticalOptions="CenterAndExpand" />
    <Entry  MaxLength="1" TextChanged="Entry_TextChanged"
       HorizontalOptions="Center"
       VerticalOptions="CenterAndExpand" />
    <Entry  MaxLength="1" TextChanged="Entry_TextChanged"
       HorizontalOptions="Center"
       VerticalOptions="CenterAndExpand" />
    <Entry  MaxLength="1" TextChanged="Entry_TextChanged"
       HorizontalOptions="Center"
       VerticalOptions="CenterAndExpand" />
</StackLayout>

And in code behind:

private void Entry_TextChanged(object sender, TextChangedEventArgs e)
{

    var entry = sender as Entry; // .. and check for null

    if (entry.Text.Length == 1)
    {
        var list = (entry.Parent as StackLayout).Children; //assumes a StackLayout
        var index = list.IndexOf(entry); // 
        var nextIndex = (index + 1) >= list.Count ? 0 : index + 1; //first or next element?
        var next = list.ElementAt(nextIndex);
        next?.Focus();
    }

}

Update:

In code behind:

public partial class MainPage : ContentPage
{

    public List<Entry> myEntryList { get; set; }
    public MainPage()
    {
        InitializeComponent();

        myEntryList = new List<Entry>();
        myEntryList.Add(this.PasswordOne);
        myEntryList.Add(this.PasswordTwo);
    }

    private void Entry_TextChanged(object sender, TextChangedEventArgs e)
    {

        var entry = sender as Entry; // .. and check for null

        if (entry.Text.Length >= 1)
        {                
            var index = myEntryList.IndexOf(entry); // 
            var nextIndex = (index + 1) >= myEntryList.Count ? 0 : index + 1; //first or next element?
            var next = myEntryList.ElementAt(nextIndex);
            next?.Focus();
        }
    }
}

And in Xaml:

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="2*" />
        <RowDefinition Height="100" />
        <RowDefinition Height="100" />
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*" />
        <ColumnDefinition Width="*" />
    </Grid.ColumnDefinitions>
    <Grid Grid.Column="1">
        <Frame CornerRadius="12">
            <BoxView Color="#EEEEEE" />
        </Frame>
        <Entry
            x:Name="PasswordOne"
            Text="PasswordOne"
            IsPassword="True"
            Keyboard="Numeric"
            MaxLength="1"
            ReturnType="Next"
           
            TextChanged="Entry_TextChanged"
            >
        </Entry>
    </Grid>

    <Grid Grid.Column="2">
        <Frame CornerRadius="12">
            <BoxView Color="#EEEEEE" />
        </Frame>
        <Entry
            x:Name="PasswordTwo"
            Text="PasswordTwo"
            IsPassword="True"
            Keyboard="Numeric"
            MaxLength="1"
            ReturnType="Next"
                            
            TextChanged="Entry_TextChanged"

            >   
        </Entry>
    </Grid>

</Grid>