Is it possible to change the text in the 'return' key on the keyboard to be either 'next' or 'done'? I have a login form with username and password. I want the return key to say 'next' when on the username field and then 'done' when on the password field but haven't seen anyway of doing this. This is for a shared project, android and iOS.
- 
                    1Just use the property "ReturnType" in your Entry. – David Jesus May 30 '20 at 19:58
4 Answers
A custom EntryRenderer can handle changing the keyboard return key description.
- iOS : - UITextFieldhas a- ReturnKeyTypeproperty that you can set to a preassigned list (see- UIReturnTypeenum).
- Android : - EntryEditTexthas a- ImeOptionsproperty that controls what the "Action" button on the keyboard does and a- SetImeActionLabelmethod that you can use to set any text string for it.
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}");
        }
    }
}
 
    
    - 73,120
- 10
- 106
- 165
- 
                    Quick question though, where does the `.GetValueFromDescription()` come from? It's not showing up for me – John May 26 '16 at 17:52
- 
                    1@Shane Ahhh... sorry about that... that is an enum extension method, same as this SO answer.. http://stackoverflow.com/a/4367868/4984832 I am not at a computer to access my source but that answer is the one that I use (and reuse) from years ago. Let my know if you have any problems with it... – SushiHangover May 26 '16 at 18:06
- 
                    So I threw that method into its own static class (not sure if thats the right thing to do or not) and it got rid of the error but now I'm getting `The type arguments for method GetValueFromDescription(this string) cannot be inferred from the usage. Try specifying the type arguments explicitly`.. any ideas why? – John May 26 '16 at 18:39
- 
                    @Shane , I explictly typed the enums to remove the `.ToString()`, doing that with a straight copy/paste of that `EnumEx` code you can : `Control.ReturnKeyType = EnumEx.GetValueFromDescription((sender as EntryExt).ReturnKeyType.ToString());` That is for iOS, for Android type is an `ImeAction` ... – SushiHangover May 26 '16 at 18:53
- 
                    @Shane I'll update the answer later to include the `GetValueFromDescription` code – SushiHangover May 26 '16 at 18:57
- 
                    Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/113067/discussion-between-shane-and-sushihangover). – John May 26 '16 at 18:57
- 
                    1@Shane Tried chat but must have missed you, I added that source to my answer.. but it would be cleaner to put it as a generic enum extension in a shared project.... but this works also.. – SushiHangover May 26 '16 at 19:07
- 
                    
- 
                    
- 
                    I believe there is a typo in your Android renderer that can lead to "unhandled exceptions": `if ((Control != null) & (e.NewElement != null))` should be `if ((Control != null) && (e.NewElement != null))`. – Derek Foulk May 24 '17 at 20:16
- 
                    
Yes latest Xamarin Forms allowed directly ReturnType as property, just need to add ReturnType in Xaml
 <Entry x:Name="myEntry" ReturnType="Done"/>
 
    
    - 924
- 2
- 12
- 34
- 
                    That's true and very useful, unfortunately it doesn't work for the `Numeric` keyboard on iOS despite it working fine on the `Numeric` keyboard for Android. I think the custom renderer for iOS would still be needed if you are in the same scenario as me with a Numeric keyboard. – iBobb Jul 17 '20 at 12:24
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.
 
    
    - 19,681
- 50
- 236
- 417
- 
                    1You forgot the line to associate the platform-specific renderers with the `BaseEntry` class. Here it is: `[assembly: ExportRenderer(typeof(BaseEntry), typeof(BaseEntryRenderer))]` – nonzaprej Jul 10 '17 at 18:25
The latest Xamarin Forms package adds the ReturnType attribute for Entry elements. It will also execute a command when the Done button is clicked. The IMEAction types for Done, Next, Search, Go and Send are all supported now.
 
    
    - 329
- 3
- 11


 
    