Share via

UITextFieldDelegate.ShouldReturn not triggered

Anonymous
2024-04-05T19:10:53.79+00:00

Hi, I'm having a problem with triggering the Completed event in custom control on iOS.

I'm implementing the CustomRadEntry as a custom control of type Entry.

public class CustomRadEntry : Entry 	
{ 		
	public event EventHandler OnCompleted;	
	public CustomRadEntry() 	
	{ }  	
	public void InvokeCompletedEvent() 	
	{ 			
		OnCompleted?.Invoke(this, null); 	
	} 
}


CustomRadEntryHandler.cs

public partial class CustomRadEntryHandler
{
	public static IPropertyMapper<CustomRadEntry, CustomRadEntryHandler> PropertyMapper = 			new PropertyMapper<CustomRadEntry, CustomRadEntryHandler>(EntryHandler.ViewMapper)
        {
            [nameof(CustomRadEntry.Text)] = MapText
        };

        public static CommandMapper<CustomRadEntry, CustomRadEntryHandler> CommandMapper = 	new(ViewCommandMapper)
        { };

        
	public CustomRadEntryHandler() : base(PropertyMapper, CommandMapper)        
	{    } 
}

CustomRadEntryHandler.MaciOS.cs

public partial class CustomRadEntryHandler : EntryHandler
	{
        protected override MauiCustomRadEntry CreatePlatformView()
        {
            return new MauiCustomRadEntry(VirtualView);
        }

        protected override void ConnectHandler(MauiTextField platformView)
        {
            base.ConnectHandler(platformView);
        }

        protected override void DisconnectHandler(MauiTextField platformView)
        {
            base.DisconnectHandler(platformView);
        }
    }


Beneath it, there is also an implementation for iOS named MauiCustomRadEntry which inherits from MauiTextField.

public class MauiCustomRadEntry : MauiTextField
	{
        private CustomRadEntry _customEntry;
        public MauiCustomRadEntry(IEntry entry)
        {
            _customEntry = (CustomRadEntry)entry;
            AddDoneButton();
            //TODO: Test if using _customEntry in this constructor is correct.
            CustomBorderUITextFieldDelegate textFieldDelegate = new 		CustomBorderUITextFieldDelegate(_customEntry);
            WeakDelegate = textFieldDelegate;
        }

        private void AddDoneButton()
        {
            var toolbar = new UIToolbar(new RectangleF(0.0f, 0.0f, 50.0f, 44.0f));

            var doneButton = new UIBarButtonItem(UIBarButtonSystemItem.Done, delegate
            {
                ResignFirstResponder();
                _customEntry.InvokeCompletedEvent();
            });

            toolbar.Items = new[] {
                        new UIBarButtonItem (UIBarButtonSystemItem.FlexibleSpace),
                        doneButton};
            InputAccessoryView = toolbar;
        }
    }

    public class CustomBorderUITextFieldDelegate : UITextFieldDelegate
    {
        private WeakReference element;

        internal Entry Element => this.element.IsAlive ? (Entry)this.element.Target : (Entry)null;

        public CustomBorderUITextFieldDelegate(Entry element) => this.element = new WeakReference((object)element);

        public override void EditingStarted(UITextField textField) => this.Element?.SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, (object)true);

        public override bool ShouldReturn(UITextField textField)
        {
            textField.ResignFirstResponder();
            if (Element is CustomRadEntry customRadEntry)
            {
                customRadEntry.InvokeCompletedEvent();
            }
            //this.Element.InvokeCompletedEvent();
            return false;
        }

        public override void EditingEnded(UITextField textField, UITextFieldDidEndEditingReason reason) => this.Element?.SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, (object)false);
    }


inside the MauiCustomRadEntry constructor, I assigned a WeakDelegate to a custom Delegate. Details of implementation is in the attached project. The Page containing and testing this control is CustomRadEntryPage.xaml.

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="dummyProj.Pages.CustomRadEntryPage"
             xmlns:controls="clr-namespace:dummyProj.CustomControls"
             Title="CustomRadEntryPage">
    <Grid>
        <controls:CustomRadEntry
            HorizontalOptions="Center"
            Text="heLLO WORLD"
            OnCompleted="CustomRadEntry_OnCompleted"
            ReturnType="Next"
            />
    </Grid>
</ContentPage>

CustomRadEntryPage.xaml.cs

public CustomRadEntryPage()
	{
		InitializeComponent();
	}

    void CustomRadEntry_OnCompleted(System.Object sender, System.EventArgs e)
    {
		Console.Write("Hit Done");
    }

If you run into the issue when an exception of delegate occurs, please add this line to CreateMauiApp() of AppDelegate.cs

UIApplication.CheckForEventAndDelegateMismatches = false;

When I hit Done, I expect it to trigger the ShouldReturn() of the delegate. In fact, it doesn't. I need it to invoke the completed event. Please have a look and can you give me a work-around to make this work?

Developer technologies | .NET | .NET Multi-platform App UI

1 answer

Sort by: Most helpful
  1. Wenyan Zhang (Shanghai Wicresoft Co,.Ltd.) 36,456 Reputation points Microsoft External Staff
    2024-04-08T05:41:12.86+00:00

    Hello,

    From the MAUI EntryHandler.iOS source code, we can see MAUI has implemented this ShouldReturn delegate method, you could overwrite it in the CustomRadEntryHandler class. Please refer to the following code:

    public partial class CustomRadEntryHandler : EntryHandler
    {
        protected override MauiCustomRadEntry CreatePlatformView()
        {
            return new MauiCustomRadEntry(VirtualView);
        }
    
    
       protected override bool OnShouldReturn(UITextField view)
        {
            CustomRadEntry customRadEntry = this.VirtualView as CustomRadEntry;
            customRadEntry.InvokeCompletedEvent();// Find the entry and invoke the completed event
            Debug.WriteLine("CustomRadEntryHandler Hit ");
          return base.OnShouldReturn(view);
    
    
        }
        protected override void ConnectHandler(MauiTextField platformView)
        {
            base.ConnectHandler(platformView);
            platformView.ShouldReturn += OnShouldReturn;
        }
    
    
       protected override void DisconnectHandler(MauiTextField platformView)
        {
            base.DisconnectHandler(platformView);
            platformView.ShouldReturn -= OnShouldReturn;
        }
    }
    

    Best Regards,

    Wenyan Zhang


    If the answer is the right solution, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment".

    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.

    Was this answer helpful?

    0 comments No comments

Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.