Focus System.ObjectDisposedException: Cannot access a disposed object

TransisTransis USMember ✭✭
edited February 12 in Xamarin.Forms

Since xamarin forms 3.5 I'm having this error when call .Focus()

JniPeerMembers.AssertSelf (Java.Interop.IJavaPeerable self)
JniPeerMembers+JniInstanceMethods.InvokeNonvirtualObjectMethod (System.String encodedMember, Java.Interop.IJavaPeerable self, Java.Interop.JniArgumentValue* parameters)
View.get_Context ()
ViewRenderer`2[TView,TNativeView].OnFocusChangeRequested (System.Object sender, Xamarin.Forms.VisualElement+FocusRequestArgs e)
(wrapper delegate-invoke) System.EventHandler`1[Xamarin.Forms.VisualElement+FocusRequestArgs].invoke_void_object_TEventArgs(object,Xamarin.Forms.VisualElement/FocusRequestArgs)
VisualElement.Focus () 

Answers

  • YorkGoYorkGo CNMember, Xamarin Team Xamurai

    Could you please post more detailed codes? or maybe share a basic demo that can reproduce the problem through online repo

  • TransisTransis USMember ✭✭

    Hi @YorkGo

    I've collected this error automatically with Microsoft App Center

    I can't reproduce it during the tests, it happens on production release with my users.

    It happens when the program call .Focus(). Every Entry field is faccing this problem, but to sample, its a button that call an Entry.Focus();

        private void LBL_UNIT_LIQ_Tapped(object sender, EventArgs e)
        {
                    TXT_PRECO_INFORMADO_UNIT?.Focus();
        }
    
  • Vinnivan2Vinnivan2 USMember ✭✭
    edited February 12

    I'm seeing the same thing - just had to roll back to 3.4.0.102999

    JniPeerMembers.AssertSelf (Java.Interop.IJavaPeerable self)
    JniPeerMembers+JniInstanceMethods.InvokeNonvirtualObjectMethod (System.String encodedMember, Java.Interop.IJavaPeerable self, Java.Interop.JniArgumentValue* parameters)
    View.get_Context ()
    ViewRenderer`2[TView,TNativeView].OnFocusChangeRequested (System.Object sender, Xamarin.Forms.VisualElement+FocusRequestArgs e)
    VisualElement.Focus ()
    AdvancedTextControl+d__275.MoveNext ()

    For simplicity say my app has two pages. Each page has a single entry. When the page is shown Focus is called on the entry. I noticed this crash just switching back and forth between the pages a couple of times.

    I hadn't seen this error prior to 3.5 and I'm unable to reproduce so far with 3.4.

  • NMackayNMackay GBInsider, University mod

    @Transis said:
    Hi @YorkGo

    I've collected this error automatically with Microsoft App Center

    I can't reproduce it during the tests, it happens on production release with my users.

    It happens when the program call .Focus(). Every Entry field is faccing this problem, but to sample, its a button that call an Entry.Focus();

        private void LBL_UNIT_LIQ_Tapped(object sender, EventArgs e)
        {
                    TXT_PRECO_INFORMADO_UNIT?.Focus();
        }
    

    I see your bug report but it would help the team if you supplied a repo project
    https://github.com/xamarin/Xamarin.Forms/issues/5202

  • JohnHardmanJohnHardman GBUniversity mod

    @Vinnivan2 said:
    I'm seeing the same thing - just had to roll back to 3.4.0.102999

    JniPeerMembers.AssertSelf (Java.Interop.IJavaPeerable self)
    JniPeerMembers+JniInstanceMethods.InvokeNonvirtualObjectMethod (System.String encodedMember, Java.Interop.IJavaPeerable self, Java.Interop.JniArgumentValue* parameters)
    View.get_Context ()
    ViewRenderer`2[TView,TNativeView].OnFocusChangeRequested (System.Object sender, Xamarin.Forms.VisualElement+FocusRequestArgs e)
    VisualElement.Focus ()
    AdvancedTextControl+d__275.MoveNext ()

    For simplicity say my app has two pages. Each page has a single entry. When the page is shown Focus is called on the entry. I noticed this crash just switching back and forth between the pages a couple of times.

    I hadn't seen this error prior to 3.5 and I'm unable to reproduce so far with 3.4.

    Are you:
    (1) calling Focus() from OnAppearing()
    (2) calling Focus() from an invocation of Device.BeginInvokeOnMainThread from a new Task started from OnAppearing()
    (3) calling Focus() from an invocation of Device.BeginInvokeOnMainThread from a new Task started from the Page's constructor
    (4) some other way?

  • Vinnivan2Vinnivan2 USMember ✭✭
    edited February 12

    Basically (1) in the case where it's crashing it's ultimately being called from OnAppearing.

    However, I'm checking the result of entry.focus() if it fails I delay 300ms and then retry from Device.BeginInvokeOnMainThread.

    It is crashing on the FIRST call to entry.focus() - it never makes it to the second delayed entry.focus() because the object disposed exception is called as shown in my stack trace.

    As mentioned this has worked flawlessly up until yesterday when I updated to 3.5 - but obviously if I'm doing something wrong would be really happy to get it corrected.

  • JohnHardmanJohnHardman GBUniversity mod
    edited February 12

    @Vinnivan2 said:
    Basically (1) in the case where it's crashing it's ultimately being called from OnAppearing.
    It is crashing on the FIRST call to entry.focus() - it never makes it to the second delayed entry.focus() because the object disposed exception is called as shown in my stack trace.

    The Entry needs to be rendered on the Page before Focus() will work (on Android, at least). Go for option (2) above, which allows rendering to complete before Focus() is called. There's still a race condition, but that's manageable.

    (caveat: I am still on 3.4, but I would expect (2) to still be the method to use on 3.5)

  • NMackayNMackay GBInsider, University mod
    edited February 12

    @Vinnivan2 said:
    Basically (1) in the case where it's crashing it's ultimately being called from OnAppearing.

    However, I'm checking the result of entry.focus() if it fails I delay 300ms and then retry from Device.BeginInvokeOnMainThread.

    It is crashing on the FIRST call to entry.focus() - it never makes it to the second delayed entry.focus() because the object disposed exception is called as shown in my stack trace.

    As mentioned this has worked flawlessly up until yesterday when I updated to 3.5 - but obviously if I'm doing something wrong would be really happy to get it corrected.

    Add a repo case and an example. Reported this issue earlier a while back, if you give a full repo it means then can assign it's priority. The issue opened doesn't easily allow the team to re-create and their time is precious.

    https://github.com/xamarin/Xamarin.Forms/issues/3834

  • Vinnivan2Vinnivan2 USMember ✭✭

    Thanks all. I have opened an issue with a repo project included.

    https://github.com/xamarin/Xamarin.Forms/issues/5238

  • JohnHardmanJohnHardman GBUniversity mod
    edited February 13

    Whilst the ObjectDisposedException might be new (I cannot remember for sure), the need to fire up a new Task from OnAppearing() in order to successfully do the Focus() call on Android has been there for a long time.

    At a simplistic level, the following will be pretty safe to use from OnAppearing to do the Focus call, although with a race condition:

                Task.Run(async () =>
                {
                    if (delayInMilliseconds > 0)
                        await Task.Delay(delayInMilliseconds);
    
                    Device.BeginInvokeOnMainThread(async () =>
                    {
                        try
                        {
                            action.Invoke();
                        }
                        catch (Exception ex)
                        {
                            if (silent)
                                await SilentlyReportExceptionAsync(ex);
                            else
                                await ReportExceptionAsync(ex);
                        }
                    });
                });
    

    In practice, I have the above as part of a larger wrapper. Also, I have page content wrapped in a ScrollView, so even before setting the focus, I will have scrolled the Entry into view. The below is a helper I use to do that, which means that the code above could be simplified. The use of IsInNativeLayout in the code below is (to me) the interesting bit.

    public static class ScrollHelper
    {
        private static bool _inUse = false;
    
        // Based on https://github.com/xamarin/Xamarin.Forms/pull/988
        public static async Task ScrollOnceInLayoutAsync(
            ScrollView scrollView,
            View view,
            bool animated = false,
            ScrollToPosition scrollToPosition = ScrollToPosition.End,
            int millisecondDelay = 1,
            int maxCycles = 10)
        {
            if ((scrollView != null) && (view != null)
                && ApplicationType.ScrollToAsyncExpectedToWork())
            {
                if (!_inUse)
                {
                    _inUse = true;
    
                    // Note that IsInNativeLayout is currently marked for Xamarin internal use only
                    int cycle = 0;
                    while ((!view.IsInNativeLayout) && (++cycle <= maxCycles))
                    {
                        await Task.Delay(TimeSpan.FromMilliseconds(millisecondDelay));
                    }
    
                    try
                    {
                        await scrollView.ScrollToAsync(view, scrollToPosition, animated);
                    }
                    catch (Exception ex)
                    {
                        await InsightsWrapper.SilentlyReportExceptionAsync(ex);
                    }
    
                    _inUse = false;
                }
            }
        }
    }
    
Sign In or Register to comment.