Change return to be next/done key in Xamarin Forms Shared Project

Yes latest Xamarin Forms allowed directly ReturnType as property, just need to add ReturnType in Xaml

 <Entry x:Name="myEntry" ReturnType="Done"/>

Here is an alternative approach, but similar to the solution of SushiHangover. It includes UWP support:

ReturnType.cs in PCL

public enum ReturnType
{
    Go,
    Next,
    Done,
    Send,
    Search
}

BaseEntry.cs in PCL

public class BaseEntry : Entry
{
    // Need to overwrite default handler because we cant Invoke otherwise
    public new event EventHandler Completed;

    public static readonly BindableProperty ReturnTypeProperty = BindableProperty.Create(
        nameof(ReturnType),
        typeof(ReturnType),
        typeof(BaseEntry),
        ReturnType.Done, 
        BindingMode.OneWay
    );

    public ReturnType ReturnType
    {
        get { return (ReturnType)GetValue(ReturnTypeProperty); }
        set { SetValue(ReturnTypeProperty, value); }
    }

    public void InvokeCompleted()
    {
        if (this.Completed != null)
            this.Completed.Invoke(this, null);
    }
}

BaseEntryRenderer.cs for Android

public class BaseEntryRenderer : EntryRenderer
{
    protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
    {
        base.OnElementChanged(e);

        BaseEntry entry = (BaseEntry)this.Element;

        if(this.Control != null)
        {
            if(entry != null)
            {
                SetReturnType(entry);

                // Editor Action is called when the return button is pressed
                Control.EditorAction += (object sender, TextView.EditorActionEventArgs args) =>
                {
                    if (entry.ReturnType != ReturnType.Next)
                        entry.Unfocus();

                    // Call all the methods attached to base_entry event handler Completed
                    entry.InvokeCompleted();
                };
            }
        }
    }

    private void SetReturnType(BaseEntry entry)
    {
        ReturnType type = entry.ReturnType;

        switch (type)
        {
            case ReturnType.Go:
                Control.ImeOptions = ImeAction.Go;
                Control.SetImeActionLabel("Go", ImeAction.Go);
                break;
            case ReturnType.Next:
                Control.ImeOptions = ImeAction.Next;
                Control.SetImeActionLabel("Next", ImeAction.Next);
                break;
            case ReturnType.Send:
                Control.ImeOptions = ImeAction.Send;
                Control.SetImeActionLabel("Send", ImeAction.Send);
                break;
            case ReturnType.Search:
                Control.ImeOptions = ImeAction.Search;
                Control.SetImeActionLabel("Search", ImeAction.Search);
                break;
            default:
                Control.ImeOptions = ImeAction.Done;
                Control.SetImeActionLabel("Done", ImeAction.Done);
                break;
        }
    }
}

BaseEntryRenderer.cs for iOS

public class BaseEntryRenderer : EntryRenderer
{
    protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
    {
        base.OnElementChanged(e);

        BaseEntry entry = (BaseEntry)this.Element;

        if (this.Control != null)
        {
            if(entry != null)
            {
                SetReturnType(entry);

                Control.ShouldReturn += (UITextField tf) =>
                {
                    entry.InvokeCompleted();
                    return true;
                };
            }
        }
    }

    private void SetReturnType(BaseEntry entry)
    {
        ReturnType type = entry.ReturnType;

        switch (type)
        {
            case ReturnType.Go:
                Control.ReturnKeyType = UIReturnKeyType.Go;
                break;
            case ReturnType.Next:
                Control.ReturnKeyType = UIReturnKeyType.Next;
                break;
            case ReturnType.Send:
                Control.ReturnKeyType = UIReturnKeyType.Send;
                break;
            case ReturnType.Search:
                Control.ReturnKeyType = UIReturnKeyType.Search;
                break;
            case ReturnType.Done:
                Control.ReturnKeyType = UIReturnKeyType.Done;
                break;
            default:
                Control.ReturnKeyType = UIReturnKeyType.Default;
                break;
        }
    }
}

BaseEntryRenderer.cs for UWP

public class BaseEntryRenderer : EntryRenderer
{
    protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
    {
        base.OnElementChanged(e);

        BaseEntry entry = (BaseEntry)this.Element;

        if(this.Control != null)
        {
            if(entry != null)
            {
                this.Control.KeyDown += (object sender, Windows.UI.Xaml.Input.KeyRoutedEventArgs eventArgs) =>
                {
                    if (eventArgs.Key == Windows.System.VirtualKey.Enter)
                    {
                        entry.InvokeCompleted();
                        // Make sure to set the Handled to true, otherwise the RoutedEvent might fire twice
                        eventArgs.Handled = true;
                    }
                };
            }
        }
    }
}

On UWP it seems not be possible to change the return key to next/done/... You need to add ExportRenderer attributes yourself for all custom renderers.

Usage

XAML file

<renderer:BaseEntry x:Name="username" Text="Username" ReturnType="Next" />
<renderer:BaseEntry x:Name="password" Text ="Password" IsPassword="true" ReturnType="Done" />

Code behind file:

this.username.Completed += (object sender, EventArgs e) => this.password.Focus();

Based on this source.


A custom EntryRenderer can handle changing the keyboard return key description.

  • iOS : UITextField has a ReturnKeyType property that you can set to a preassigned list (see UIReturnType enum).

  • Android : EntryEditText has a ImeOptions property that controls what the "Action" button on the keyboard does and a SetImeActionLabel method that you can use to set any text string for it.

enter image description here

Usage Example of the custom Entry/EntryRenderer:

new EntryExt {
    Text = "Next Key",
    ReturnKeyType = ReturnKeyTypes.Next
},
new EntryExt {
    Text = "Done Key",
    ReturnKeyType = ReturnKeyTypes.Done
}

A Xamarin.Forms custom Entry class:

namespace YourNameSpaceHere
{
    public class EntryExt : Entry
    {
        public const string ReturnKeyPropertyName = "ReturnKeyType";

        public EntryExt() { }

        public static readonly BindableProperty ReturnKeyTypeProperty = BindableProperty.Create(
            propertyName: ReturnKeyPropertyName,
            returnType: typeof(ReturnKeyTypes),
            declaringType: typeof(EntryExt),
            defaultValue: ReturnKeyTypes.Done);

        public ReturnKeyTypes ReturnKeyType
        {
            get { return (ReturnKeyTypes)GetValue(ReturnKeyTypeProperty); }
            set { SetValue(ReturnKeyTypeProperty, value); }
        }
    }

    // Not all of these are support on Android, consult EntryEditText.ImeOptions
    public enum ReturnKeyTypes : int
    {
        Default,
        Go,
        Google,
        Join,
        Next,
        Route,
        Search,
        Send,
        Yahoo,
        Done,
        EmergencyCall,
        Continue
    }
}

iOS custom EntryRenderer:

[assembly: ExportRenderer(typeof(Entry), typeof(EntryExtRenderer_iOS))]
namespace KeyboardDone.iOS
{
    public class EntryExtRenderer_iOS : EntryRenderer
    {
        public EntryExtRenderer_iOS() { }

        protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
        {
            base.OnElementChanged(e);
            if ((Control != null) && (e.NewElement != null))
                Control.ReturnKeyType = (e.NewElement as EntryExt).ReturnKeyType.GetValueFromDescription();
        }

        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);
            if (e.PropertyName == EntryExt.ReturnKeyPropertyName)
            {
                D.WriteLine($"{(sender as EntryExt).ReturnKeyType.ToString()}");
                Control.ReturnKeyType = (sender as EntryExt).ReturnKeyType.GetValueFromDescription();
            }
        }
    }

    public static class EnumExtensions
    {
        public static UIReturnKeyType GetValueFromDescription(this ReturnKeyTypes value)
        {
            var type = typeof(UIReturnKeyType);
            if (!type.IsEnum) throw new InvalidOperationException();
            foreach (var field in type.GetFields())
            {
                var attribute = Attribute.GetCustomAttribute(field,
                    typeof(DescriptionAttribute)) as DescriptionAttribute;
                if (attribute != null)
                {
                    if (attribute.Description == value.ToString())
                        return (UIReturnKeyType)field.GetValue(null);
                }
                else
                {
                    if (field.Name == value.ToString())
                        return (UIReturnKeyType)field.GetValue(null);
                }
            }
            throw new NotSupportedException($"Not supported on iOS: {value}");
        }
    }
}

Android custom EntryRenderer:

[assembly: ExportRenderer(typeof(Entry), typeof(EntryExtRenderer_Droid))]
namespace KeyboardDone.Droid
{
    public class EntryExtRenderer_Droid : EntryRenderer
    {
        public EntryExtRenderer_Droid() { }

        protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
        {
            base.OnElementChanged(e);
            if ((Control != null) && (e.NewElement != null))
            {
                var entryExt = (e.NewElement as EntryExt);
                Control.ImeOptions = entryExt.ReturnKeyType.GetValueFromDescription();
                // This is hackie ;-) / A Android-only bindable property should be added to the EntryExt class 
                Control.SetImeActionLabel(entryExt.ReturnKeyType.ToString(), Control.ImeOptions);
            }
        }
        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);
            if (e.PropertyName == EntryExt.ReturnKeyPropertyName)
            {
                var entryExt = (sender as EntryExt);
                Control.ImeOptions = entryExt.ReturnKeyType.GetValueFromDescription();
                // This is hackie ;-) / A Android-only bindable property should be added to the EntryExt class 
                Control.SetImeActionLabel(entryExt.ReturnKeyType.ToString(), Control.ImeOptions);
            }
        }

    }
    public static class EnumExtensions
    {
        public static ImeAction GetValueFromDescription(this ReturnKeyTypes value)
        {
            var type = typeof(ImeAction);
            if (!type.IsEnum) throw new InvalidOperationException();
            foreach (var field in type.GetFields())
            {
                var attribute = Attribute.GetCustomAttribute(field,
                    typeof(DescriptionAttribute)) as DescriptionAttribute;
                if (attribute != null)
                {
                    if (attribute.Description == value.ToString())
                        return (ImeAction)field.GetValue(null);
                }
                else
                {
                    if (field.Name == value.ToString())
                        return (ImeAction)field.GetValue(null);
                }
            }
            throw new NotSupportedException($"Not supported on Android: {value}");
        }
    }
}