Search code examples
xamarinxamarin.formsxamarin-community-toolkit

Xamarin Forms: Is there a ViewDisappeared event for ViewElement?


I have been trying to dispose a ViewElement/View when the Page displaying it is no longer on top of the NavigationStack in Xamarin Forms.

It seems there is a new effect called LifecycleEffect from Xamarin Community Toolkit that would be able to do just that, but the Unloaded event does not fire when a new page is placed on top of the NavigationStack (and therefore the view-element will not be removed and not trigger the event).

Does anybody know and event that I can bind to on a View that allows me to determine it is not displaying, but is still in the NavigationStack somewhere?

I really want the View to do this itself and not use the Page for that.

Thanks in advance :)


Solution

  • AFAIK no platform does that, because it is a bit expensive (for something that is almost never needed): a method would have to be executed on every visual element on the screen, every time a page hides or shows.

    The only solution (I am aware of) is to have this view add an "event handler" to the page it is in, and then have the page's OnDisappearing invoke that event handler. Define an Interface that exposes an "AddActionOnDisappearing" method. Implement that interface on pages that might have this view.

    First Version: a hand-coded solution:

    public interface IActionOnDisappearing
    {
        void AddActionOnDisappearing(Action action)
        {
        }
    }
    
    public partial class MyPage : IActionOnDisappearing
    {
        ...
        public void AddActionOnDisappearing(Action action)
        {
            ... remember the action somewhere ...
        }
        protected override void OnDisappearing()
        {
            ... call that action or event handler ...
            ...
        }
    }
    
    public partial class MySpecialView
    {
        // constructor
        void MySpecialView(IActionOnDisappearing owningPage)
        {
            ...
            TellMeThePage(owningPage);
        }
    
        public void TellMeThePage(IActionOnDisappearing owningPage)
        {
            owningPage.AddActionOnDisappearing( () =>
               ... whatever you need to do ...
               );
        }
    
    }
    

    Note that the view constructor takes a parameter. An alternative is to let XAML build the views on the page, give that view an x:Name attribute, then in page's constructor, after InitializeComponent, connect the two:

    public partial class MyPage : IActionOnDisappearing
    {
        // constructor
        void MyPage()
        {
            InitializeComponent();
            this.NameOfMySpecialView.TellMeThePage(this);
            ...
        }
    }
    
    
    public partial class MySpecialView
    {
        // constructor
        void MySpecialView()
        {
            ...
        }
    
        public void TellMeThePage(IActionOnDisappearing owningPage)
        ...
    }
    

    I've left out details of the event handler that MyPage would need. Google for information on that topic.


    Second Version

    The solution above requires the Page to be aware it has such a View. And to give that View a name. And add a code line to the constructor.

    To make it easier to code, define this somewhere:

    public static void TellMySpecialDescendents(Page owner)
    {
        ... search all children and their children
        ... any that are of type `MySpecialView`, call
            ((MySpecialView)child).TellMeThePage(owner);
    }
    

    Define class BasePage, that implements IActionOnDisappearing. Any class that might have such a view, inherit from BasePage.

    And call TellMySpecialDescendents from constructor of such a page. After InitializeComponent of course.

    If you have more than one such view-type that needs to know about its owner, define an Interface:

    public interface IViewOnDisappearing
    {
        void TellMeThePage(IActionOnDisappearing owningPage);
    }
    
    public partial class MySpecialView : IViewOnDisappearing
    ...
    

    And replace MySpecialView uses with IViewOnDisappearing.