7

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.

MegaMiley
  • 478
  • 1
  • 5
  • 19

2 Answers2

9

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.

MegaMiley
  • 478
  • 1
  • 5
  • 19
3

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.

Brandon Minnick
  • 13,342
  • 15
  • 65
  • 123