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 :
UITextField
has aReturnKeyType
property that you can set to a preassigned list (seeUIReturnType
enum).Android :
EntryEditText
has aImeOptions
property that controls what the "Action" button on the keyboard does and aSetImeActionLabel
method 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