Hi,
First I'm a huge fan of Xamarin & Xamarin.Forms, it is simply amazing !
My question is :
I am creating a XAML ContentView which has animations looping while the IsVisible property is true and it works fine.
However, how can I detect from this custom control that its containing page has been removed or is not visible anymore when navigating back or forward to another page ? It seems that when navigating back to my first page my component (on the 2nd page) is not destroyed.
Thank you very much,
Have a nice day :-) !
The View
class doesn't have that lifecycle event at the moment. You'll have to trigger it somehow from a Page
.
Again, what you need to do is use the Page
's OnAppearing
override. That's the only hook you have for now. The Page
will just have to somehow communicate that information to the view.
Answers
http://iosapi.xamarin.com/index.aspx?link=M:Xamarin.Forms.Page.OnAppearing()
override OnAppearing
Thanks for your answer :-) !
Yes I know I can use OnAppearing from my Page containing my ContentView, but what I want is to be able to do this from the inside of my ContentView. In other words I want it to be independant and start animating by itself and stop animating by itself too.
To be a little more specific :
it seems that there is no WPF's UserControl Loaded & Unloaded events equivalent in Xamarin.Forms ContentView. Is that correct :-) ?
The
View
class doesn't have that lifecycle event at the moment. You'll have to trigger it somehow from aPage
.I have the same problem. I'd like to start and stop a timer inside a view when the view is closed.
I can listen to the PropertyChanged. There is a "Renderer" property changed event which is fired every time the view appears or disappears. However, this renderer is not exposed in the API. Hence, I can't tell whether the view is appearing or disappearing.
Again, what you need to do is use the
Page
'sOnAppearing
override. That's the only hook you have for now. ThePage
will just have to somehow communicate that information to the view.When is this going to be fixed?
+1
I've stumbled upon this issue and decided to create my own solution to the problem.
For future googlers, here is how I solved this: https://www.pshul.com/2018/03/27/xamarin-forms-add-views-lifecycle-events/
@AlexP,
Nicely done. Maybe you can contact Xamarin and ask them to add this to their next version ;-)
I was able to get around this as welll see below.
ContentView someView = new ContentView();
someView.LayoutChanged += (s,e) =
{
Device.BeginInvokeOnMainThread(async () =>
{
//event fired the moment contentview is displayed!!!!
});
}
I found that overriding
OnParentSet()
works great.@jonathannichols thank you! Your solution worked great for my situation.
OnParentSet() fires BEFORE the OnAppearing so if you wanted something in the Nav Stack or anything you're out of luck.
Thanks @jonathannichols bang on!
the layout changed thing works great @Xamtastic @jonathannichols - is there an equivalent for unloading we can use so we can -= out the +=? Otherwise I'm worried we'll get multiple event handlers attached as users continue using the app.
You could test if the bindingcontext has been set to null perhaps and unhook your event handlers, I would suspect the code above causes leaks (pretty certain), Prism allows hooks into ContentViews if they are registered as PartialViews so you can implement IDestructible and unhook subscribers, handlers, behaviors etc easily, if not using Prism you might have to get the parent page to call a cleanup method on the child contentview. Prism mantains an internal dictionary of partial views for pages so it knows to call cleanup, if the control is a reusable composite control, checking the binding context has been released may be an alternative.
Thanks @NMackay, I always appreciate your thoughtful answers on this forum, nice to get one to my own question.
The Prism approach sounds great, but the project is too far down the line for a refactor to use that, so I've ended up going for a combination of your "binding context changed" and "clean up in the parent page" approaches.
I probably should have mentioned this, but the ContentView I need to unregister is used as the template for a collectionview item, so we have to throw recycle-ability into the mix. The binding context can change a lot, when the user scrolls, rather than just getting assigned and unassigned once each.
On top of this complexity, it's the binding context for a picker view inside the collectionview item that I'm working with. (I'd like to set the items and selected index changed logic in a pure MVVM way but I'm finding I'm having to do it inside the view instead of my viewmodel, because otherwise the collection view recycling nukes my binding context when I scroll up down the list and the items get thrown away, and the picker becomes empty)
My solution boils down to this:
In the ContentView.xaml.cs
Then in the parent page xaml.cs
It seems quite verbose but I think I'm covering all my bases, plus it works and it's fast. Do you reckon I've missed anything fundamental / would you do it differently?
I'd love to avoid all this by having access to loading / unloading consistent lifecycle events for contentviews but neither of the two most popular workarounds online work for me in my case reliably on android and ios https://www.pshul.com/2018/03/27/xamarin-forms-add-views-lifecycle-events/ and https://www.ston.is/xamarin-forms-secrets-renderer-property/) perhaps because of the collectionview / picker combination. And it seems that the github ticket to add this into Xamarin.Forms core is a ways off.
Thanks again!
Yeah, cell recycling makes things more complicated, I think your parent cleanup is probably the safest approach, the renderer for the control would have to take care of the unhooking of handlers if it can't be handled in shared code.
Good way to test if to take a snapshot once the page is in memory
Pop the page
Force garbage collection (a snapshot in the Xamarin profiler does a GC.Collect), usually it takes 2 or three snapshots
If the memory doesn't reduce and there's life instances of bound objects you know you have an issue
You can use the VS memory profiler if you have a UWP project to do this but you have to call GC.Collect with that profiler from memory.
There's no page lifecycle events built in to child sub views so unfortunately you have to take care of it, means a more stable app.