Removing and adding of TextChanged event handler

RicoWidmerRicoWidmer USMember ✭✭

I have a problem with the TextChanged event of an entry.
I added an event handler for this event to check the entered character for correctness. If it is not allowed I remove it and rewrite the string into the entry. This rewriting again calls the TextChanged event. To avoid the second call, I remove the event handler before setting the new text and add it after setting the new text:
entry.TextChanged -= MyTextChangedEvent; entry.Text = updatedText; entry.TextChanged += MyTextChangedEvent;
This unfortunately doesn't seem to work. MyTextChangedEvent is still invoked twice. When I add a short Task.Delay, it works though:
entry.TextChanged -= MyTextChangedEvent; entry.Text = updatedText; await Task.Delay(1); entry.TextChanged += MyTextChangedEvent;
Can anybody tell me what I do wrong? From the solutions found in this forum the Task.Delay() shouldn't be necessary.
Thank you in advance.


Best Answer


  • RicoWidmerRicoWidmer USMember ✭✭

    Thank you very much for your response. I will next time post such questions in the right forum.
    Is using Task.Yield in such a situation a clean solution or is there a better way to update text in an entry from within the TextChanged event handler? How would you accomplish such text corrections?

  • adamkempadamkemp USInsider, Developer Group Leader mod

    I'm not sure there is a better way using the API provided. In iOS there is a callback designed specifically for this use case (I don't remember about Android). This code seems clean enough to use without cringing, though.

  • dev.kramdev.kram PHMember ✭✭

    @adamkemp said:
    If this is a Xamarin.Forms question then you should post it in the Xamarin.Forms forum.

    The problem is that the Text property is a bindable property, and therefore setting it goes through all of the mechanics of bindable properties. One result of this is that if you try to set the property within a value changed callback for that property then the new setter is deferred until after the completion of the callback. Therefore your unsubscribing from the event has no effect because by the time the property actually gets its new value you've already added it back.

    That's why the delay works. It moves the new setter out of the context of the original value change, and therefore the new value is set immediately.

    However, any time you have Task.Delay(1) you should probably be using Task.Yield() instead. An arbitrary delay is a code smell because it indicates a possible race condition. Task.Yield does nothing more than yield to the run loop and come back ASAP (no arbitrary delay). It's the preferred way to do stuff like this.

    You have realized it is a code smell yet you still encourage the use of any code that has nothing to do with the logic just for the sake of it to "work". My colleagues uses Task.Delay(100) anytime his code won't work and it's all over in the code base I will be taking over. I am refactoring his code now to adapt to the latest Xamarin update and it's been an issue for me since removing it would require me to spend more time debugging it. I would not be surprise if someone brought the phrase "if it's not broken, don't fix it"

  • adamkempadamkemp USInsider, Developer Group Leader mod
    I don’t think I did encourage it. I just said if you think you need `Task.Delay` then you probably really want `Task.Yield`. I don’t think this particular use case required either, but I still don’t have the full context.

    Sorry you have to deal with a bad code base. Keep fighting the good fight.
Sign In or Register to comment.