Search code examples
c#xamlxamarin.forms

Change focus on entry completed with behavior in Xamarin.Forms


I want to change focus from one entry to other entry.

At first, I changed with Focus() method for all entry. But I think it's not good code, because the more entry, the more difficult to edit.

So I found reference from here https://adenearnshaw.com/focus-next-element-on-enter/

First, create behavior

public class SetFocusOnEntryCompletedBehavior : Behavior
{
    public static readonly BindableProperty TargetElementProperty
       = BindableProperty.Create(nameof(TargetElement), typeof(VisualElement), typeof(SetFocusOnEntryCompletedBehavior));

    public VisualElement TargetElement
    {
        get => (VisualElement)GetValue(TargetElementProperty);
        set => SetValue(TargetElementProperty, value);
    }
        
    protected override void OnAttachedTo(BindableObject bindable)
    {
        base.OnAttachedTo(bindable);
        
        if (bindable is Entry entry)
            entry.Completed += Entry_Completed;
    }

    protected override void OnDetachingFrom(BindableObject bindable)
    {
        if (bindable is Entry entry)
            entry.Completed -= Entry_Completed;

        base.OnDetachingFrom(bindable);
    }

    private void Entry_Completed(object sender, EventArgs e)
    {
        TargetElement?.Focus();
    }
}

Next, implement in xaml

<ContentPage x:Class="NextFormFieldSample.Forms.MainPage"
             xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:behaviors="clr-namespace:NextFormFieldSample.Forms.Behaviors">
    <StackLayout Margin="20">   
        
        <Entry Placeholder="Field 1">
            <Entry.Behaviors>
                <behaviors:SetFocusOnEntryCompletedBehavior TargetElement="{x:Reference Entry2}" />
            </Entry.Behaviors>
        </Entry>

        <Entry x:Name="Entry2" Placeholder="Field 2"/>  
        
    </StackLayout>
</ContentPage>

But!!!! I have to add one more thing for using this.

I have to receive result after entry completed event; If the result is true, focus changed. If not, focus stayed again.

I tried to make behaviors. But it's very difficult for me.

Are there anybody help me?


Solution

  • That is, with some result from database, if the result is true, focus is changed. But if that is false, focus stay on same entry. Not only just change the focus of entry, but watch the result from database.

    Then you can add some logical judgment to the event Entry_Completed.

    Please refer to the following code:

    <ContentPage.Content> 
        <StackLayout Margin="20">
      
            <Entry x:Name="Entry1"
               Placeholder="Field 1"
               Completed="Entry_Completed"
               TabIndex="1">
    
            </Entry>
       
            <Entry x:Name="Entry2"
               Placeholder="Field 2"
               Completed="Entry_Completed"
               TabIndex="2">
    
            </Entry>        
    
            <Entry x:Name="Entry3"
               Placeholder="Field 3"
               Completed="Entry_Completed"
               TabIndex="3">
            </Entry>
    
            <Entry x:Name="Entry4"
               Placeholder="Field 4"
               Completed="Entry_Completed"
               TabIndex="4" />
        </StackLayout>
    </ContentPage.Content>
    

    Event Entry_Completed in YourPage.xaml.cs

       private void Entry_Completed(object sender, EventArgs e) 
        {
            var entry = sender as Entry; // .. and check for null
    
            // get result from database here
    
            if (entry.Text.Length == 2) // here I used string's length(2) as the logical condition
            {
                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();
            }
          }