Xamarin Forms Entry invoke Completed event

10,506

Solution 1

Fixed the issue by changing

entryExt.InvokeCompleted(); 

inside the EditorAction to

((IEntryController)Element).SendCompleted(); 

which sends out the completed event back to the Entry base class.

Solution 2

I've implemented a similar method of implementing this that allows the Return button to be extended to implement any type: Go, Next, Done, Send and Search.

Sample App

Here is a Xamarin.Forms app where I've implemented this approach. Feel free to download the repo and try it out!

1. Create Custom Entry

In the Xamarin.Forms PCL, create a Custom Entry

public class EntryWithCustomKeyboardReturnButton : Entry
{
    public new event EventHandler Completed;

    public static readonly BindableProperty ReturnTypeProperty =
        BindableProperty.Create<EntryWithCustomKeyboardReturnButton, ReturnType>(s => s.ReturnType, ReturnType.Done);

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

    public void InvokeCompleted()
    {
        Completed?.Invoke(this, null);
    }
}

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

2. Create iOS Custom Renderer

In the iOS PCL, create this custom renderer

[assembly: ExportRenderer(typeof(EntryWithCustomKeyboardReturnButton), typeof(EntryWithCustomKeyboardReturnButtonCustomRenderer))]
namespace Sample.iOS
{
    public class EntryWithCustomKeyboardReturnButtonCustomRenderer : EntryRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs e)
        {
            base.OnElementChanged(e);

            var customEntry = Element as EntryWithCustomKeyboardReturnButton;

            if (Control != null && customEntry != null)
            {
                SetKeyboardButtonType(customEntry.ReturnType);

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

        void SetKeyboardButtonType(ReturnType returnType)
        {
            switch (returnType)
            {
                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;
            }
        }
    }
}

3. Create Android Custom Renderer

In the Android PCL, create this custom renderer

[assembly: ExportRenderer(typeof(EntryWithCustomKeyboardReturnButton), typeof(EntryWithCustomKeyboardReturnButtonCustomRenderer))]
namespace Sample.Droid
{
    public class EntryWithCustomKeyboardReturnButtonCustomRenderer : EntryRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs e)
        {
            base.OnElementChanged(e);

            var customEntry = Element as EntryWithCustomKeyboardReturnButton;

            if (Control != null && customEntry != null)
            {
                SetKeyboardButtonType(customEntry.ReturnType);

                Control.EditorAction += (object sender, TextView.EditorActionEventArgs args) =>
                {
                    if (customEntry?.ReturnType != ReturnType.Next)
                        customEntry?.Unfocus();

                    customEntry?.InvokeCompleted();
                };
            }
        }

        void SetKeyboardButtonType(ReturnType returnType)
        {
            switch (returnType)
            {
                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;
            }
        }
    }
}

4. Implement the Completed Event for Entry in Xamarin.Forms PCL

Here is a link to some sample code that shows how to implement the Completed Event in the Xamarin.Forms PCL.

Share:
10,506

Related videos on Youtube

MegaMiley
Author by

MegaMiley

Updated on September 14, 2022

Comments

  • MegaMiley
    MegaMiley over 1 year

    I'm currently working on a login and registration page within Xamarin Forms and after changing the done button of the keyboard to next and go on the last one I am no longer receiving the Completed event on Android (working fine on iOS). In the custom renderer I can catch the Control.EditorAction event which now acts the same as the Completed event but I can't seem to invoke the Completed event on the entry itself.

    Within the EntryRenderer

    Control.EditorAction += (object sender, TextView.EditorActionEventArgsargs) =>
    {
        if (entryExt.ReturnKeyType != ReturnKeyTypes.Next)
            entryExt.Unfocus();
    
        // Call all the methods attached to base_entry event handler Completed
        entryExt.InvokeCompleted();
    };
    

    And within the EntryExt (which extends the Entry directly)

    public void InvokeCompleted()
    {
        Completed?.Invoke(this, null);
    }
    

    But the Completed event cannot be invoked due to the error

    Error CS0070: The event `Xamarin.Forms.Entry.Completed' can only appear on the left hand side of += or -= when used outside of the type `Xamarin.Forms.Entry'
    

    Is there a way to invoke the Completed event? I'd rather not have a separate event handler for this within my views.