2
votes

In my Xamarin app, on PIN Page, there are 8 Entry Fields, 4 for PIN and 4 for Confirm PIN (here I just paste 2).

When user enter a value in a field, the cursor focus on the next field and so on. But if they enter value mistakenly and wants to clear it, they can't go back to the last entry by pressing Backspace Button. They have to press on that entry field for focus.

Below is the Code for Entry Fields. How can I go back / focus on the last entry field by pressing Backspace button?

.xml

<Entry
    x:Name="PasswordOne"
    Text="{Binding PasswordOne}"
    IsPassword="True"
    Keyboard="Numeric"
    MaxLength="1"
    ReturnType="Next"
    TextChanged="Entry_FieldChanged" />

.xml.cs

public PasswordPage()
{
    InitializeComponent();

    entryList = new List<Entry>();
    entryList.Add(PasswordOne);
    entryList.Add(PasswordTwo);

    entryList.Add(ConfirmPasswordOne);
    entryList.Add(ConfirmPasswordTwo);
}

private void Entry_FieldChanged(object sender, TextChangedEventArgs e)
{
    var entry = sender as Entry;

    const string numregex = "^[0-9]+$";

    if (!(!System.Text.RegularExpressions.Regex.IsMatch(entry.Text, numregex) || entry.Text == ""))
    {
        if (entry.Text.Length >= 1)
        {
            var index = entryList.IndexOf(entry);
            var nextIndex = (index + 1) >= entryList.Count ? 0 : index + 1;
            var next = entryList.ElementAt(nextIndex);
            if (entry == ConfirmPasswordTwo)
            { }
            else
            {
                next?.Focus();
            }
        }
    }
    else
    {
        var index = entryList.IndexOf(entry);
        var prevIndex = (index) == 0 ? 0 : index - 1;
        var prev = entryList.ElementAt(prevIndex);
        if (entry == PasswordOne)
        { }
        else
        {
            prev?.Focus();
        }
    }
}

UPDATE

CustomEntry.xml.cs

namespace Mobile.App
{
    public class CustomEntry : Entry
    {

        public delegate void BackspaceEventHandler(object sender, EventArgs e);

        public event BackspaceEventHandler OnBackspace;
        public CustomEntry() { }

        public void OnBackspacePressed()
        {
            OnBackspace?.Invoke(null, null);
        }

    }
}

CustomEntryRenderer

[assembly: ExportRenderer(typeof(CustomEntry), typeof(CustomEntryRenderer))]
namespace Mobile.App.Droid
{
    public class CustomEntryRenderer : EntryRenderer, IInputFilter
    {
        public CustomEntryRenderer(Context context) : base(context)
        {
        }

        public ICharSequence FilterFormatted(ICharSequence source, int start, int end, ISpanned dest, int dstart, int dend)
        {
            if (string.IsNullOrWhiteSpace(source.ToString()))
            {
                var entry = (CustomEntry)Element;
                entry.OnBackspacePressed();
            }
            return source;
        }

        protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
        {
            base.OnElementChanged(e);

            if (Control == null)
            {
                return;
            }

            if (Control != null)
            {
                int length = Control.Text.Length;
                Control.SetSelection(length);
                SetColors();
            };

            Control.SetFilters(new IInputFilter[] { this });
        }

        private void SetColors()
        {
            Control.SetBackgroundColor(Android.Graphics.Color.Transparent);
        }
    }
}

.xml.cs

public partial class PasswordPage : ContentPage
{

    public List<Entry> entryList { get; set; }

    public PasswordPage()
    {
        InitializeComponent();
        ValidatePassword();

        entryList = new List<Entry>();
        entryList.Add(PasswordOne);
        entryList.Add(PasswordTwo);

        entryList.Add(ConfirmPasswordOne);
        entryList.Add(ConfirmPasswordTwo);
    }

    Entry entry;
    private void Entry_FieldChanged(object sender, TextChangedEventArgs e)
    {
        entry = sender as Entry;

        const string numregex = "^[0-9]+$";

        if (!(!System.Text.RegularExpressions.Regex.IsMatch(entry.Text, numregex) || entry.Text == ""))
        {
            if (entry.Text.Length >= 1)
            {
                var index = entryList.IndexOf(entry);
                var nextIndex = (index + 1) >= entryList.Count ? 0 : index + 1;
                var next = entryList.ElementAt(nextIndex);
                if (entry == ConfirmPasswordTwo)
                { }
                else
                {
                    next?.Focus();
                }
            }
        }
        //else
        //{
        //    var index = entryList.IndexOf(entry);
        //    var prevIndex = (index) == 0 ? 0 : index - 1;
        //    var prev = entryList.ElementAt(prevIndex);
        //    if (entry == PasswordOne)
        //    { }
        //    else
        //    {
        //        prev?.Focus();
        //    }
        //}
    }

    private void CustomEntry_OnBackspace(object sender, EventArgs e)
    {
        const string numregex = "^[0-9]+$";

        if (!(System.Text.RegularExpressions.Regex.IsMatch(entry.Text, numregex) || entry.Text == ""))
        {
            var last = entryList.ElementAt(entryList.Count - 1);
            last?.Focus();
        }
        else
        {
            return;
        }
    }
}

.xml

<local:CustomEntry
    x:Name="PasswordOne"
    IsPassword="True"
    Keyboard="Numeric"
    MaxLength="1"
    ReturnType="Next"
    TextChanged="Entry_FieldChanged"
    OnBackspace="CustomEntry_OnBackspace"
    Unfocused="Password_Unfocused" />
1
What is your Backspace button? In the keyboard? When you press this button, do you wanna to focus on the last?Wendy Zang - MSFT
yes, yes and yesuser14139029

1 Answers

0
votes

If you want to focus to the last entry when you press the Backspace button, you could use the custom renderer.

Custom Entry:

 public class CustomEntry : Entry
{
     
    public delegate void BackspaceEventHandler(object sender, EventArgs e);
   
    public event BackspaceEventHandler OnBackspace;
    public CustomEntry() { }

    public void OnBackspacePressed()
    {
        OnBackspace?.Invoke(null, null);
    }

}

Custom renderer:

[assembly: ExportRenderer(typeof(CustomEntry), typeof(CustomEntryRenderer))]
namespace Test.Droid
{
public class CustomEntryRenderer : EntryRenderer, Android.Text.IInputFilter
{
    public CustomEntryRenderer(Context context) : base(context)
    {
    }

    public ICharSequence FilterFormatted(ICharSequence source, int start, int end, ISpanned dest, int dstart, int dend)
    {
        if (string.IsNullOrWhiteSpace(source.ToString()))
        {
            var entry = (CustomEntry)Element;
            entry.OnBackspacePressed();
        }
        return source;
    }

    protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
    {
        base.OnElementChanged(e);

        if (Control == null)
        {
            return;
        }

        Control.SetFilters(new IInputFilter[] { this });

    }

    

}
}

Xaml:

  <local:CustomEntry
            x:Name="PasswordOne"
            IsPassword="True"
            Keyboard="Numeric"
            MaxLength="1"
            OnBackspace="CustomEntry_OnBackspace"
            ReturnType="Next"
            Text="{Binding PasswordOne}"
            TextChanged="Entry_FieldChanged" />

        <local:CustomEntry
            x:Name="PasswordTwo"
            IsPassword="True"
            Keyboard="Numeric"
            MaxLength="1"
            OnBackspace="CustomEntry_OnBackspace"
            ReturnType="Next"
            Text="{Binding PasswordTwo}"
            TextChanged="Entry_FieldChanged" />

        <local:CustomEntry
            x:Name="ConfirmPasswordOne"
            IsPassword="True"
            Keyboard="Numeric"
            MaxLength="1"
            OnBackspace="CustomEntry_OnBackspace"
            ReturnType="Next"
            Text="{Binding ConfirmPasswordOne}"
            TextChanged="Entry_FieldChanged" />

        <local:CustomEntry
            x:Name="ConfirmPasswordTwo"
            IsPassword="True"
            Keyboard="Numeric"
            MaxLength="1"
            OnBackspace="CustomEntry_OnBackspace"
            ReturnType="Done"
            Text="{Binding ConfirmPasswordTwo}"
            TextChanged="Entry_FieldChanged" />

Code behind:

   public List<Entry> entryList { get; set; }
    public Page3()
    {
        InitializeComponent();
        entryList = new List<Entry>();
        entryList.Add(PasswordOne);
        entryList.Add(PasswordTwo);

        entryList.Add(ConfirmPasswordOne);
        entryList.Add(ConfirmPasswordTwo);
    }
    Entry entry;
    private void Entry_FieldChanged(object sender, TextChangedEventArgs e)
    {
        entry = sender as Entry;

        const string numregex = "^[0-9]+$";

        if (!(!System.Text.RegularExpressions.Regex.IsMatch(entry.Text, numregex) || entry.Text == ""))
        {
            if (entry.Text.Length >= 1)
            {
                var index = entryList.IndexOf(entry);
                var nextIndex = (index + 1) >= entryList.Count ? 0 : index + 1;
                var next = entryList.ElementAt(nextIndex);
                if (entry == ConfirmPasswordTwo)
                { }
                else
                {
                    next?.Focus();
                }
            }
        }
        //else
        //{
        //    var index = entryList.IndexOf(entry);
        //    var prevIndex = (index) == 0 ? 0 : index - 1;
        //    var prev = entryList.ElementAt(prevIndex);
        //    if (entry == PasswordOne)
        //    { }
        //    else
        //    {
        //        prev?.Focus();
        //    }
        //}
    }

    private void CustomEntry_OnBackspace(object sender, EventArgs e)
    {
        const string numregex = "^[0-9]+$";

        if (!(!System.Text.RegularExpressions.Regex.IsMatch(entry.Text, numregex) || entry.Text == ""))
        {
            var last = entryList.ElementAt(entryList.Count - 1);
            last?.Focus();
        }
        else
        {
            return;
        }
    }

Please note that we need to comment the else part of Entry_FieldChanged.

Updated:

On .xml.cs page I can't use Entry as a base class. It returns an error. Currently, I'm using ContentPage

Create a new class and do not use the Entry as base class directly for the ContentPage.

If you still have the error, please provide the error with the error message.

for local:Entry, I add the xmlns:local="http://xamarin.com/schemas/2014/forms" as a reference, right?

No, local is the location for CustomEntry. For me, the CustomEntry is in the Xamarin.forms project named Test.

 xmlns:local="clr-namespace:Test"

I've already an Entry Custom Renderer (Below is the code) in my project. The OnBackspacePressed(); says that Entry doens't contain the definition for OnBackspacePressed().

Do you create the custom entry first? I define the OnBackspacePressed in my CustomEntry control.

without tapping/touching/clicking on entry no. 2, if u click backspace button from any entry, u just go back to previous entry, again WITHOUT tapping/clicking/touching manually on any entry

 private void CustomEntry_OnBackspace(object sender, EventArgs e)
    {
        const string numregex = "^[0-9]+$";

        if (!(!System.Text.RegularExpressions.Regex.IsMatch(entry.Text, numregex) || entry.Text == ""))
        {
            //var last = entryList.ElementAt(entryList.Count - 1);
            //last?.Focus();
            var index = entryList.IndexOf(entry);
            var pre = entryList.ElementAt(index - 1);
            pre?.Focus();
        }
        else
        {
            return;
        }
    }