Xamarin.Android 8.2
Xamarin.Forms 2.5
How do I, or should I let a ContentView know the life cycle state of its containing Page (e.g. Appearing, Disappearing, Sleeping)
I am learning how to create a reusable Xamarin ContentView. I decide to create a control <LabelCarousel/>
that will display its children label one after another.
The problem is I have no idea how to stop the background timer that switches the content
<local:LabelCarousel>
<Label>Hello</Label>
<Label>Hola</Label>
</local:LabelCarousel>
namespace XamarinStart.Views
{
[XamlCompilation(XamlCompilationOptions.Compile)]
[ContentProperty("LabelContainer")]
public partial class LabelCarousel : ContentView
{
[Browsable(true), EditorBrowsable(EditorBrowsableState.Always)]
[Description("Labels"), Category("Data")]
public List<Element> LabelContainer { get; } = new List<Element>();
private int _index = 0;
private Timer _timer = new Timer(2000);
public LabelCarousel ()
{
InitializeComponent ();
_timer.Elapsed += TimerEvent;
_timer.Start();
}
private void TimerEvent(object sender, ElapsedEventArgs e)
{
if (_index >= LabelContainer.Count)
{
_index = 0;
}
var selected = LabelContainer[_index++];
ChangeLabel((Label)selected);
}
private void ChangeLabel(Label lbl)
{
Device.BeginInvokeOnMainThread(async () =>
{
await this.FadeTo(0);
Content = lbl;
await this.FadeTo(1);
});
}
}
}
When I put this app into background, or push another activity on the top of this one, the timer is still running, wasting the CPU resource. Is there any good idea to let the reusable ContentView notified when the parent page changes the state?
https://forums.xamarin.com/discussion/38989/contentview-lifecycle https://forums.xamarin.com/discussion/65140/we-need-a-way-for-contentview-and-viewcells-to-monitor-their-own-lifecycle
Thank you!
I figured out one possibility myself by using the class extension to add AttachLifecycleToPage
method to all Element
object.
Basically, the principle behind the extension is tracing up the Parent along the UI tree. However, when the ContentView is initiated (i.e., ctor is called), it may not be attached to a parent yet, or its parent (if any) is not attached to a page. That's why I listen to the PropertyChanged event of the Element whose Parent is temporarily null. Once the Element gets attached, the searching process continues.
Note that the AttachLifecycleToPage
should not be synchronous in the Element ctor, which will causes a dead lock.
namespace XamarinStart.Extensions
{
public static class PageAwareExtension
{
public static void AttachLifecycleToPage(this Element element, EventHandler onAppearing = null,
EventHandler onDisappearing = null)
{
var task = new Task(() =>
{
var page = GetPage(element);
if (page == null)
{
return;
}
if (onAppearing != null)
{
page.Appearing += onAppearing;
}
if (onDisappearing != null)
{
page.Disappearing += onDisappearing;
}
});
task.Start();
return;
}
public static Page GetPage(this Element element, int timeout = -1)
{
if (element is Page) return (Page)element;
Element el = element;
// go up along the UI tree
do
{
if (el.Parent == null)
{
// either not attached to any parent, or no intend to attach to any page (is that possible?)
var signal = new AutoResetEvent(false);
PropertyChangedEventHandler handler = (object sender, PropertyChangedEventArgs args) =>
{
// https://github.com/xamarin/Xamarin.Forms/blob/master/Xamarin.Forms.Core/Element.cs
// Setting Parent property is tracked
Element senderElement = (Element) sender;
if (args.PropertyName == "Parent" && senderElement.Parent != null)
{
signal.Set();
}
};
el.PropertyChanged += handler;
var gotSignal = signal.WaitOne(timeout);
if (!gotSignal)
{
throw new TimeoutException("Cannot find the parent of the element");
}
el.PropertyChanged -= handler;
} // if parent is null
el = el.Parent;
} while (! (el is Page));
return (Page) el;
}
}
}
public LabelCarousel ()
{
InitializeComponent ();
_timer.Elapsed += TimerEvent;
_timer.Start();
this.AttachLifecycleToPage(OnAppearing, OnDisappearing);
}
private void OnDisappearing(object sender, EventArgs eventArgs)
{
_timer.Stop();
}
private void OnAppearing(object sender, EventArgs eventArgs)
{
_timer.Start();
}
Well, I am not sure if this dirty hack can cause some side effect. I hope that eventually the official release will have the ContentView with full life cycle support.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With