Skip to content

Commit e5e8987

Browse files
kamukamu
kamu
authored and
kamu
committed
fix OnDetached logic
1 parent 27fd776 commit e5e8987

File tree

5 files changed

+111
-29
lines changed

5 files changed

+111
-29
lines changed

AiForms.Effects.Droid/AiEffectBase.cs

+102-19
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Reflection;
55
using System.Linq;
66
using Xamarin.Forms;
7+
using Android.App;
78

89
namespace AiForms.Effects.Droid
910
{
@@ -14,7 +15,6 @@ public abstract class AiEffectBase : PlatformEffect
1415

1516
IVisualElementRenderer _renderer;
1617
bool _isDisposed = false;
17-
WeakReference<Page> _pageRef;
1818

1919
protected bool IsDisposed {
2020
get {
@@ -47,28 +47,32 @@ protected bool IsNullOrDisposed{
4747
protected override void OnAttached()
4848
{
4949
var visual = Element as VisualElement;
50+
var page = visual.Navigation.NavigationStack.LastOrDefault();
51+
if(page == null)
52+
{
53+
page = visual.Navigation.ModalStack.LastOrDefault();
54+
if (page == null) {
55+
// In case the element in DataTemplate, NavigationProxycan't be got.
56+
// Instead of it, the page dismissal is judged by whether the BindingContext is null.
57+
Element.BindingContextChanged += BindingContextChanged;
58+
return;
59+
}
60+
}
5061

51-
var page = visual.Navigation.NavigationStack.LastOrDefault() ??
52-
visual.Navigation.ModalStack.LastOrDefault();
53-
54-
if (page == null)
55-
return;
56-
57-
page.Disappearing += Page_Disappearing;
58-
59-
_pageRef = new WeakReference<Page>(page);
62+
// To call certainly a OnDetached method when the page is popped,
63+
// it executes the process removing all the effects in the page at once with Attached bindable property.
64+
if (!GetIsRegistered(page))
65+
{
66+
SetIsRegistered(page, true);
67+
}
6068
}
6169

6270
protected override void OnDetached()
6371
{
6472
System.Diagnostics.Debug.WriteLine($"Detached {GetType().Name} from {Element.GetType().FullName}");
65-
if (_pageRef != null && _pageRef.TryGetTarget(out var page))
66-
{
67-
page.Disappearing -= Page_Disappearing;
68-
}
73+
Element.BindingContextChanged -= BindingContextChanged;
6974

7075
_renderer = null;
71-
_pageRef = null;
7276
}
7377

7478

@@ -125,16 +129,95 @@ Func<object, object> CreateGetField(Type t)
125129
return lambda.Compile();
126130
}
127131

128-
void Page_Disappearing(object sender, EventArgs e)
132+
void BindingContextChanged(object sender, EventArgs e)
129133
{
134+
if (Element.BindingContext != null)
135+
return;
136+
130137
// For Android, when a page is popped, OnDetached is automatically not called. (when iOS, it is called)
131-
// So, made the Page.Disappearing event subscribe in advance
132-
// and make the effect manually removed when the page is popped.
133-
if (IsAttached && !IsDisposed)
138+
// So, made the BindingContextChanged event subscribe in advance
139+
// and make the effect manually removed when the BindingContext is null.
140+
// However, there is the problem that it isn't called when the BindingContext remains null all along.
141+
// The above solution is to use NavigationPage.Popped or Application.ModalPopping event.
142+
// That's why the following code runs only when the element is in a DataTemplate.
143+
if (IsAttached)
134144
{
135145
var toRemove = Element.Effects.OfType<AiRoutingEffectBase>().FirstOrDefault(x => x.EffectId == ResolveId);
136146
Device.BeginInvokeOnMainThread(() => Element.Effects.Remove(toRemove));
137147
}
138148
}
149+
150+
internal static readonly BindableProperty IsRegisteredProperty =
151+
BindableProperty.CreateAttached(
152+
"IsRegistered",
153+
typeof(bool),
154+
typeof(AiEffectBase),
155+
default(bool),
156+
propertyChanged: IsRegisteredPropertyChanged
157+
);
158+
159+
internal static void SetIsRegistered(BindableObject view, bool value)
160+
{
161+
view.SetValue(IsRegisteredProperty, value);
162+
}
163+
164+
internal static bool GetIsRegistered(BindableObject view)
165+
{
166+
return (bool)view.GetValue(IsRegisteredProperty);
167+
}
168+
169+
static void IsRegisteredPropertyChanged(BindableObject bindable, object oldValue, object newValue)
170+
{
171+
if (!(bool)newValue) return;
172+
173+
var page = bindable as Page;
174+
175+
if (page.Parent is NavigationPage navi)
176+
{
177+
navi.Popped += NaviPopped;
178+
}
179+
else
180+
{
181+
Xamarin.Forms.Application.Current.ModalPopping += ModalPopping;
182+
}
183+
184+
void NaviPopped(object sender, NavigationEventArgs e)
185+
{
186+
if (e.Page != page)
187+
return;
188+
189+
navi.Popped -= NaviPopped;
190+
191+
RemoveEffects();
192+
}
193+
194+
void ModalPopping(object sender, ModalPoppingEventArgs e)
195+
{
196+
if (e.Modal != page)
197+
return;
198+
199+
Xamarin.Forms.Application.Current.ModalPopping -= ModalPopping;
200+
201+
RemoveEffects();
202+
}
203+
204+
void RemoveEffects()
205+
{
206+
foreach (var child in page.Descendants())
207+
{
208+
foreach (var effect in child.Effects.OfType<AiRoutingEffectBase>())
209+
{
210+
Device.BeginInvokeOnMainThread(() => child.Effects.Remove(effect));
211+
}
212+
}
213+
214+
foreach(var effect in page.Effects.OfType<AiRoutingEffectBase>())
215+
{
216+
Device.BeginInvokeOnMainThread(() => page.Effects.Remove(effect));
217+
}
218+
219+
page.ClearValue(IsRegisteredProperty);
220+
}
221+
}
139222
}
140223
}

Tests/AiEffects.TestApp/AiEffects.TestApp/ViewModels/ForInvestigationViewModel.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@ public ForInvestigationViewModel(INavigationService navigationService)
1515
BackColor.Value = Color.Blue;
1616

1717
var toggle = false;
18-
GoCommand.Subscribe(_ =>
18+
GoCommand.Subscribe(async _ =>
1919
{
2020
//BackColor.Value = toggle ? Color.Blue : Color.Green;
2121
//toggle = !toggle;
22-
navigationService.GoBackAsync(null, false);
22+
await navigationService.NavigateAsync("MainPage",null,true);
2323
});
2424
}
2525
}

Tests/AiEffects.TestApp/AiEffects.TestApp/ViewModels/ViewCellPageViewModel.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ public ViewCellPageViewModel(INavigationService navigationService, IPageDialogSe
1717
}
1818

1919
TestCommand.Subscribe(async _=>{
20-
await navigationService.GoBackAsync(null);
21-
//await pageDlg.DisplayAlertAsync("", "Tap", "OK");
20+
//await navigationService.GoBackAsync(null);
21+
await pageDlg.DisplayAlertAsync("", "Tap", "OK");
2222

2323
});
2424
}

Tests/AiEffects.TestApp/AiEffects.TestApp/Views/AddTouchPage.xaml.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ public partial class AddTouchPage : ContentPage
1010
public AddTouchPage()
1111
{
1212
InitializeComponent();
13-
13+
BindingContext = this;
1414
var recognizer = AddTouch.GetRecognizer(container);
1515

1616
recognizer.TouchBegin += (sender, e) => {

Tests/AiEffects.TestApp/AiEffects.TestApp/Views/ViewCellPage.xaml

+4-5
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,9 @@
1010
ef:AddCommand.LongCommand="{Binding TestCommand}"
1111
ef:AddText.Text="Abc"
1212
ef:AlterLineHeight.Multiple="1.5"
13-
ef:SizeToFit.CanExpand="true"
14-
15-
/>
16-
<!--<ListView ItemsSource="{Binding ItemsSource}" x:Name="listview" VerticalOptions="FillAndExpand">
13+
ef:SizeToFit.CanExpand="true" />
14+
15+
<ListView ItemsSource="{Binding ItemsSource}" x:Name="listview" VerticalOptions="FillAndExpand">
1716
<ListView.ItemTemplate>
1817
<DataTemplate>
1918
<ViewCell>
@@ -26,6 +25,6 @@
2625
</ViewCell>
2726
</DataTemplate>
2827
</ListView.ItemTemplate>
29-
</ListView>-->
28+
</ListView>
3029
</StackLayout>
3130
</ContentPage>

0 commit comments

Comments
 (0)