androidxamarinxamarin.formscustom-renderer

Xamarin Forms : disposed object problem with an Android renderer


Using an Android renderer for a Frame inside a page in Xamarin Forms, I need to change the position of this object after the size allocation of the page. The page being in a tab in a Shell, when I change tabs and I come back I get the exception 'Cannot access a disposed object' in the renderer.

The exception occurs on this line of UpdatePos:

SetY(20);

My problem has been reproduced with the code below :

The page :

public partial class TestPage : ContentPage
{
    public partial class Container : Frame
    {
        public delegate void PosChangedEvent();
        public event PosChangedEvent HandlerPosUpdated;
        public void Update()
        {
            HandlerPosUpdated?.Invoke();
        }
    }
    Container _container = null;
    public TestPage()
    {
        InitializeComponent();
        _container = new Container()
        {
            Content = new myView()
        };
        main_layout.Children.Add(_container); 
    }
    protected override void OnSizeAllocated(double width, double height)
    {
        base.OnSizeAllocated(width, height);
        _container.Update();
    }
}

The renderer :

public class ContainerRenderer : ViewRenderer<Frame, Android.Views.View>
{
    public ContainerRenderer(Context context) : base(context)
    {}
    public void UpdatePos()
    {
        SetY(20); // System.ObjectDisposedException: 'Cannot access a disposed object. Object name: 'ContainerRenderer'.'

    }
    protected override void OnElementChanged(ElementChangedEventArgs<Frame> e)
    {
        base.OnElementChanged(e);

        if (e.NewElement != null)
        {
            TestPage.Container view = e.NewElement as TestPage.Container;
            if (view != null)
            {
                view.HandlerPosUpdated += UpdatePos;
            }
        }
        if (e.OldElement != null)
        {
            TestPage.Container view = e.OldElement as TestPage.Container;
            if (view != null)
            {
                view.HandlerPosUpdated -= UpdatePos;
            }
        }
    }
}

How this exception could be avoided ?

Any hints are welcome!


Solution

  • Remove that handler when the custom renderer is disposed:

    private bool disposedValue;
    
    protected override void Dispose(bool disposing)
    {
        if (!disposedValue)
        {
            if (disposing)
            {
                RemoveHandlerPosUpdated();
            }
    
            disposedValue = true;
        }
    
        base.Dispose(disposing);
    }
    
    private void RemoveHandlerPosUpdated()
    {
        if (Element != null)
        {
            TestPage.Container view = Element as TestPage.Container;
            if (view != null)
            {
                view.HandlerPosUpdated -= UpdatePos;
            }
        }
    
    }
    

    If that doesn't fix it, then may need to do something in TestPage.Container class, to remove any handlers attached to HandlerPosUpdated. Details TBD.