diff --git a/AiForms.Effects.Droid/AiEffectBase.cs b/AiForms.Effects.Droid/AiEffectBase.cs index 4d36bff..5fcb669 100644 --- a/AiForms.Effects.Droid/AiEffectBase.cs +++ b/AiForms.Effects.Droid/AiEffectBase.cs @@ -4,6 +4,7 @@ using System.Reflection; using System.Linq; using Xamarin.Forms; +using Android.App; namespace AiForms.Effects.Droid { @@ -14,7 +15,6 @@ public abstract class AiEffectBase : PlatformEffect IVisualElementRenderer _renderer; bool _isDisposed = false; - WeakReference _pageRef; protected bool IsDisposed { get { @@ -47,28 +47,32 @@ protected bool IsNullOrDisposed{ protected override void OnAttached() { var visual = Element as VisualElement; + var page = visual.Navigation.NavigationStack.LastOrDefault(); + if(page == null) + { + page = visual.Navigation.ModalStack.LastOrDefault(); + if (page == null) { + // In case the element in DataTemplate, NavigationProxycan't be got. + // Instead of it, the page dismissal is judged by whether the BindingContext is null. + Element.BindingContextChanged += BindingContextChanged; + return; + } + } - var page = visual.Navigation.NavigationStack.LastOrDefault() ?? - visual.Navigation.ModalStack.LastOrDefault(); - - if (page == null) - return; - - page.Disappearing += Page_Disappearing; - - _pageRef = new WeakReference(page); + // To call certainly a OnDetached method when the page is popped, + // it executes the process removing all the effects in the page at once with Attached bindable property. + if (!GetIsRegistered(page)) + { + SetIsRegistered(page, true); + } } protected override void OnDetached() { System.Diagnostics.Debug.WriteLine($"Detached {GetType().Name} from {Element.GetType().FullName}"); - if (_pageRef != null && _pageRef.TryGetTarget(out var page)) - { - page.Disappearing -= Page_Disappearing; - } + Element.BindingContextChanged -= BindingContextChanged; _renderer = null; - _pageRef = null; } @@ -125,16 +129,95 @@ Func CreateGetField(Type t) return lambda.Compile(); } - void Page_Disappearing(object sender, EventArgs e) + void BindingContextChanged(object sender, EventArgs e) { + if (Element.BindingContext != null) + return; + // For Android, when a page is popped, OnDetached is automatically not called. (when iOS, it is called) - // So, made the Page.Disappearing event subscribe in advance - // and make the effect manually removed when the page is popped. - if (IsAttached && !IsDisposed) + // So, made the BindingContextChanged event subscribe in advance + // and make the effect manually removed when the BindingContext is null. + // However, there is the problem that it isn't called when the BindingContext remains null all along. + // The above solution is to use NavigationPage.Popped or Application.ModalPopping event. + // That's why the following code runs only when the element is in a DataTemplate. + if (IsAttached) { var toRemove = Element.Effects.OfType().FirstOrDefault(x => x.EffectId == ResolveId); Device.BeginInvokeOnMainThread(() => Element.Effects.Remove(toRemove)); } } + + internal static readonly BindableProperty IsRegisteredProperty = + BindableProperty.CreateAttached( + "IsRegistered", + typeof(bool), + typeof(AiEffectBase), + default(bool), + propertyChanged: IsRegisteredPropertyChanged + ); + + internal static void SetIsRegistered(BindableObject view, bool value) + { + view.SetValue(IsRegisteredProperty, value); + } + + internal static bool GetIsRegistered(BindableObject view) + { + return (bool)view.GetValue(IsRegisteredProperty); + } + + static void IsRegisteredPropertyChanged(BindableObject bindable, object oldValue, object newValue) + { + if (!(bool)newValue) return; + + var page = bindable as Page; + + if (page.Parent is NavigationPage navi) + { + navi.Popped += NaviPopped; + } + else + { + Xamarin.Forms.Application.Current.ModalPopping += ModalPopping; + } + + void NaviPopped(object sender, NavigationEventArgs e) + { + if (e.Page != page) + return; + + navi.Popped -= NaviPopped; + + RemoveEffects(); + } + + void ModalPopping(object sender, ModalPoppingEventArgs e) + { + if (e.Modal != page) + return; + + Xamarin.Forms.Application.Current.ModalPopping -= ModalPopping; + + RemoveEffects(); + } + + void RemoveEffects() + { + foreach (var child in page.Descendants()) + { + foreach (var effect in child.Effects.OfType()) + { + Device.BeginInvokeOnMainThread(() => child.Effects.Remove(effect)); + } + } + + foreach(var effect in page.Effects.OfType()) + { + Device.BeginInvokeOnMainThread(() => page.Effects.Remove(effect)); + } + + page.ClearValue(IsRegisteredProperty); + } + } } } diff --git a/Tests/AiEffects.TestApp/AiEffects.TestApp/ViewModels/ForInvestigationViewModel.cs b/Tests/AiEffects.TestApp/AiEffects.TestApp/ViewModels/ForInvestigationViewModel.cs index b8daba6..ec2eadf 100644 --- a/Tests/AiEffects.TestApp/AiEffects.TestApp/ViewModels/ForInvestigationViewModel.cs +++ b/Tests/AiEffects.TestApp/AiEffects.TestApp/ViewModels/ForInvestigationViewModel.cs @@ -15,11 +15,11 @@ public ForInvestigationViewModel(INavigationService navigationService) BackColor.Value = Color.Blue; var toggle = false; - GoCommand.Subscribe(_ => + GoCommand.Subscribe(async _ => { //BackColor.Value = toggle ? Color.Blue : Color.Green; //toggle = !toggle; - navigationService.GoBackAsync(null, false); + await navigationService.NavigateAsync("MainPage",null,true); }); } } diff --git a/Tests/AiEffects.TestApp/AiEffects.TestApp/ViewModels/ViewCellPageViewModel.cs b/Tests/AiEffects.TestApp/AiEffects.TestApp/ViewModels/ViewCellPageViewModel.cs index 3a07d3f..0817db5 100644 --- a/Tests/AiEffects.TestApp/AiEffects.TestApp/ViewModels/ViewCellPageViewModel.cs +++ b/Tests/AiEffects.TestApp/AiEffects.TestApp/ViewModels/ViewCellPageViewModel.cs @@ -17,8 +17,8 @@ public ViewCellPageViewModel(INavigationService navigationService, IPageDialogSe } TestCommand.Subscribe(async _=>{ - await navigationService.GoBackAsync(null); - //await pageDlg.DisplayAlertAsync("", "Tap", "OK"); + //await navigationService.GoBackAsync(null); + await pageDlg.DisplayAlertAsync("", "Tap", "OK"); }); } diff --git a/Tests/AiEffects.TestApp/AiEffects.TestApp/Views/AddTouchPage.xaml.cs b/Tests/AiEffects.TestApp/AiEffects.TestApp/Views/AddTouchPage.xaml.cs index ccd5f98..57254bc 100644 --- a/Tests/AiEffects.TestApp/AiEffects.TestApp/Views/AddTouchPage.xaml.cs +++ b/Tests/AiEffects.TestApp/AiEffects.TestApp/Views/AddTouchPage.xaml.cs @@ -10,7 +10,7 @@ public partial class AddTouchPage : ContentPage public AddTouchPage() { InitializeComponent(); - + BindingContext = this; var recognizer = AddTouch.GetRecognizer(container); recognizer.TouchBegin += (sender, e) => { diff --git a/Tests/AiEffects.TestApp/AiEffects.TestApp/Views/ViewCellPage.xaml b/Tests/AiEffects.TestApp/AiEffects.TestApp/Views/ViewCellPage.xaml index 513ab9b..e931687 100644 --- a/Tests/AiEffects.TestApp/AiEffects.TestApp/Views/ViewCellPage.xaml +++ b/Tests/AiEffects.TestApp/AiEffects.TestApp/Views/ViewCellPage.xaml @@ -10,10 +10,9 @@ ef:AddCommand.LongCommand="{Binding TestCommand}" ef:AddText.Text="Abc" ef:AlterLineHeight.Multiple="1.5" - ef:SizeToFit.CanExpand="true" - - /> - +