4
4
using System . Reflection ;
5
5
using System . Linq ;
6
6
using Xamarin . Forms ;
7
+ using Android . App ;
7
8
8
9
namespace AiForms . Effects . Droid
9
10
{
@@ -14,7 +15,6 @@ public abstract class AiEffectBase : PlatformEffect
14
15
15
16
IVisualElementRenderer _renderer ;
16
17
bool _isDisposed = false ;
17
- WeakReference < NavigationPage > _navigationRef ;
18
18
19
19
protected bool IsDisposed {
20
20
get {
@@ -34,18 +34,45 @@ protected bool IsDisposed {
34
34
}
35
35
}
36
36
37
+ protected bool IsNullOrDisposed {
38
+ get {
39
+ if ( Element . BindingContext == null ) {
40
+ return true ;
41
+ }
42
+
43
+ return IsDisposed ;
44
+ }
45
+ }
46
+
37
47
protected override void OnAttached ( )
38
48
{
39
49
var visual = Element as VisualElement ;
40
- var naviCandidate = visual . Navigation . NavigationStack . FirstOrDefault ( ) ? . Parent as NavigationPage ;
41
- if ( naviCandidate != null )
50
+ var page = visual . Navigation . NavigationStack . LastOrDefault ( ) ;
51
+ if ( page == null )
42
52
{
43
- naviCandidate . Popped += PagePopped ;
44
- _navigationRef = new WeakReference < NavigationPage > ( naviCandidate ) ;
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
+ }
45
60
}
46
61
47
- // Use not Popped but Popping because it is too late.
48
- Xamarin . Forms . Application . Current . ModalPopping += ModalPopping ;
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
+ }
68
+ }
69
+
70
+ protected override void OnDetached ( )
71
+ {
72
+ System . Diagnostics . Debug . WriteLine ( $ "Detached { GetType ( ) . Name } from { Element . GetType ( ) . FullName } ") ;
73
+ Element . BindingContextChanged -= BindingContextChanged ;
74
+
75
+ _renderer = null ;
49
76
}
50
77
51
78
@@ -102,32 +129,94 @@ Func<object, object> CreateGetField(Type t)
102
129
return lambda . Compile ( ) ;
103
130
}
104
131
105
- void PagePopped ( object sender , NavigationEventArgs e )
132
+ void BindingContextChanged ( object sender , EventArgs e )
133
+ {
134
+ if ( Element . BindingContext != null )
135
+ return ;
136
+
137
+ // For Android, when a page is popped, OnDetached is automatically not called. (when iOS, it is called)
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 )
144
+ {
145
+ var toRemove = Element . Effects . OfType < AiRoutingEffectBase > ( ) . FirstOrDefault ( x => x . EffectId == ResolveId ) ;
146
+ Device . BeginInvokeOnMainThread ( ( ) => Element . Effects . Remove ( toRemove ) ) ;
147
+ }
148
+ }
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 )
106
160
{
107
- Clear ( ) ;
161
+ view . SetValue ( IsRegisteredProperty , value ) ;
108
162
}
109
163
110
- void ModalPopping ( object sender , ModalPoppingEventArgs e )
164
+ internal static bool GetIsRegistered ( BindableObject view )
111
165
{
112
- Clear ( ) ;
166
+ return ( bool ) view . GetValue ( IsRegisteredProperty ) ;
113
167
}
114
168
115
- void Clear ( )
169
+ static void IsRegisteredPropertyChanged ( BindableObject bindable , object oldValue , object newValue )
116
170
{
117
- if ( _navigationRef != null && _navigationRef . TryGetTarget ( out var navi ) )
171
+ if ( ! ( bool ) newValue ) return ;
172
+
173
+ var page = bindable as Page ;
174
+
175
+ if ( page . Parent is NavigationPage navi )
118
176
{
119
- navi . Popped -= PagePopped ;
177
+ navi . Popped += NaviPopped ;
178
+ }
179
+ else
180
+ {
181
+ Xamarin . Forms . Application . Current . ModalPopping += ModalPopping ;
120
182
}
121
- Xamarin . Forms . Application . Current . ModalPopping -= ModalPopping ;
122
- _navigationRef = null ;
123
183
124
- // For Android, when a page is popped, OnDetached is automatically not called. (when iOS, it is called)
125
- // So, made the Popped & ModalPopped event subscribe in advance
126
- // and make the effect manually removed when the page is popped.
127
- if ( IsAttached && ! IsDisposed )
184
+ void NaviPopped ( object sender , NavigationEventArgs e )
128
185
{
129
- var toRemove = Element . Effects . OfType < AiRoutingEffectBase > ( ) . FirstOrDefault ( x => x . EffectId == ResolveId ) ;
130
- Element . Effects . Remove ( toRemove ) ;
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 ) ;
131
220
}
132
221
}
133
222
}
0 commit comments