11using System . Reactive . Subjects ;
2- using ReactiveUI ;
32
43namespace Stellar ;
54
6- #pragma warning disable CA1001
7- public abstract class ViewManager < TViewModel >
5+ public abstract class ViewManager < TViewModel > : IDisposable
86 where TViewModel : class
9- #pragma warning restore CA1001
107{
118 private readonly Lazy < Subject < LifecycleEvent > > _lifecycleEvents ;
129
@@ -18,33 +15,44 @@ public abstract class ViewManager<TViewModel>
1815
1916 private bool _controlsBound ;
2017
21- public IObservable < Unit > Initialized => _lifecycleEvents . Value . Where ( x => x == LifecycleEvent . Initialized ) . SelectUnit ( ) . AsObservable ( ) ;
18+ private bool _disposed = false ;
2219
23- public IObservable < Unit > Activated => _lifecycleEvents . Value . Where ( x => x == LifecycleEvent . Activated ) . SelectUnit ( ) . AsObservable ( ) ;
20+ public IObservable < Unit > Initialized => _disposed ? Observable . Empty < Unit > ( ) : _lifecycleEvents . Value . Where ( x => x == LifecycleEvent . Initialized ) . SelectUnit ( ) . AsObservable ( ) ;
2421
25- public IObservable < Unit > Attached => _lifecycleEvents . Value . Where ( x => x == LifecycleEvent . Attached ) . SelectUnit ( ) . AsObservable ( ) ;
22+ public IObservable < Unit > Activated => _disposed ? Observable . Empty < Unit > ( ) : _lifecycleEvents . Value . Where ( x => x == LifecycleEvent . Activated ) . SelectUnit ( ) . AsObservable ( ) ;
2623
27- public IObservable < Unit > IsAppearing => _lifecycleEvents . Value . Where ( x => x == LifecycleEvent . IsAppearing ) . SelectUnit ( ) . AsObservable ( ) ;
24+ public IObservable < Unit > Attached => _disposed ? Observable . Empty < Unit > ( ) : _lifecycleEvents . Value . Where ( x => x == LifecycleEvent . Attached ) . SelectUnit ( ) . AsObservable ( ) ;
2825
29- public IObservable < Unit > IsDisappearing => _lifecycleEvents . Value . Where ( x => x == LifecycleEvent . IsDisappearing ) . SelectUnit ( ) . AsObservable ( ) ;
26+ public IObservable < Unit > IsAppearing => _disposed ? Observable . Empty < Unit > ( ) : _lifecycleEvents . Value . Where ( x => x == LifecycleEvent . IsAppearing ) . SelectUnit ( ) . AsObservable ( ) ;
3027
31- public IObservable < Unit > Detached => _lifecycleEvents . Value . Where ( x => x == LifecycleEvent . Detached ) . SelectUnit ( ) . AsObservable ( ) ;
28+ public IObservable < Unit > IsDisappearing => _disposed ? Observable . Empty < Unit > ( ) : _lifecycleEvents . Value . Where ( x => x == LifecycleEvent . IsDisappearing ) . SelectUnit ( ) . AsObservable ( ) ;
3229
33- public IObservable < Unit > Deactivated => _lifecycleEvents . Value . Where ( x => x == LifecycleEvent . Deactivated ) . SelectUnit ( ) . AsObservable ( ) ;
30+ public IObservable < Unit > Detached => _disposed ? Observable . Empty < Unit > ( ) : _lifecycleEvents . Value . Where ( x => x == LifecycleEvent . Detached ) . SelectUnit ( ) . AsObservable ( ) ;
3431
35- public IObservable < Unit > Disposed => _lifecycleEvents . Value . Where ( x => x == LifecycleEvent . Disposed ) . SelectUnit ( ) . AsObservable ( ) ;
32+ public IObservable < Unit > Deactivated => _disposed ? Observable . Empty < Unit > ( ) : _lifecycleEvents . Value . Where ( x => x == LifecycleEvent . Deactivated ) . SelectUnit ( ) . AsObservable ( ) ;
3633
37- public IObservable < LifecycleEvent > LifecycleEvents => _lifecycleEvents . Value . AsObservable ( ) ;
34+ public IObservable < Unit > Disposed => _disposed ? Observable . Empty < Unit > ( ) : _lifecycleEvents . Value . Where ( x => x == LifecycleEvent . Disposed ) . SelectUnit ( ) . AsObservable ( ) ;
3835
39- public IObservable < Unit > NavigatedTo => _navigationEvents . Value . Where ( x => x == NavigationEvent . NavigatedTo ) . SelectUnit ( ) . AsObservable ( ) ;
36+ public IObservable < LifecycleEvent > LifecycleEvents => _disposed ? Observable . Empty < LifecycleEvent > ( ) : _lifecycleEvents . Value . AsObservable ( ) ;
4037
41- public IObservable < Unit > NavigatedFrom => _navigationEvents . Value . Where ( x => x == NavigationEvent . NavigatedFrom ) . SelectUnit ( ) . AsObservable ( ) ;
38+ public IObservable < Unit > NavigatedTo => _disposed ? Observable . Empty < Unit > ( ) : _navigationEvents . Value . Where ( x => x == NavigationEvent . NavigatedTo ) . SelectUnit ( ) . AsObservable ( ) ;
4239
43- public IObservable < NavigationEvent > NavigationEvents => _navigationEvents . Value . AsObservable ( ) ;
40+ public IObservable < Unit > NavigatedFrom => _disposed ? Observable . Empty < Unit > ( ) : _navigationEvents . Value . Where ( x => x == NavigationEvent . NavigatedFrom ) . SelectUnit ( ) . AsObservable ( ) ;
41+
42+ public IObservable < NavigationEvent > NavigationEvents => _disposed ? Observable . Empty < NavigationEvent > ( ) : _navigationEvents . Value . AsObservable ( ) ;
4443
4544 public bool Maintain { get ; set ; }
4645
47- public bool ControlsBound => Volatile . Read ( ref _controlsBound ) ;
46+ public bool ControlsBound
47+ {
48+ get
49+ {
50+ lock ( _bindingLock )
51+ {
52+ return _controlsBound ;
53+ }
54+ }
55+ }
4856
4957 public ViewManager ( )
5058 {
@@ -54,8 +62,53 @@ public ViewManager()
5462 _navigationEvents = new ( ( ) => new Subject < NavigationEvent > ( ) . DisposeWith ( _controlBindings ) , LazyThreadSafetyMode . ExecutionAndPublication ) ;
5563 }
5664
65+ public void Dispose ( )
66+ {
67+ Dispose ( true ) ;
68+ GC . SuppressFinalize ( this ) ;
69+ }
70+
71+ protected virtual void Dispose ( bool disposing )
72+ {
73+ if ( _disposed )
74+ {
75+ return ;
76+ }
77+
78+ if ( disposing )
79+ {
80+ lock ( _bindingLock )
81+ {
82+ _controlBindings . Dispose ( ) ;
83+
84+ // Dispose lazy-created subjects if they were created
85+ if ( _lifecycleEvents . IsValueCreated )
86+ {
87+ _lifecycleEvents . Value . Dispose ( ) ;
88+ }
89+
90+ if ( _navigationEvents . IsValueCreated )
91+ {
92+ _navigationEvents . Value . Dispose ( ) ;
93+ }
94+ }
95+ }
96+
97+ _disposed = true ;
98+ }
99+
100+ private void ThrowIfDisposed ( )
101+ {
102+ if ( _disposed )
103+ {
104+ throw new ObjectDisposedException ( nameof ( ViewManager < TViewModel > ) ) ;
105+ }
106+ }
107+
57108 public void RegisterBindings ( IStellarView < TViewModel > view )
58109 {
110+ ThrowIfDisposed ( ) ;
111+
59112 lock ( _bindingLock )
60113 {
61114 if ( _controlsBound )
@@ -76,6 +129,8 @@ public void RegisterBindings(IStellarView<TViewModel> view)
76129
77130 public void UnregisterBindings ( IStellarView < TViewModel > view )
78131 {
132+ ThrowIfDisposed ( ) ;
133+
79134 lock ( _bindingLock )
80135 {
81136 if ( Maintain || ! _controlsBound )
@@ -93,6 +148,8 @@ public void UnregisterBindings(IStellarView<TViewModel> view)
93148
94149 public virtual void HandleActivated ( IStellarView < TViewModel > view )
95150 {
151+ ThrowIfDisposed ( ) ;
152+
96153 view . RegisterViewModelBindings ( ) ;
97154
98155 RegisterBindings ( view ) ;
@@ -102,6 +159,8 @@ public virtual void HandleActivated(IStellarView<TViewModel> view)
102159
103160 public virtual void HandleDeactivated ( IStellarView < TViewModel > view )
104161 {
162+ ThrowIfDisposed ( ) ;
163+
105164 OnLifecycle ( view , LifecycleEvent . Deactivated ) ;
106165
107166 UnregisterBindings ( view ) ;
@@ -110,6 +169,8 @@ public virtual void HandleDeactivated(IStellarView<TViewModel> view)
110169 public virtual void PropertyChanged < TView > ( TView view , string ? propertyName = null )
111170 where TView : IViewFor < TViewModel >
112171 {
172+ ThrowIfDisposed ( ) ;
173+
113174 if ( propertyName == nameof ( IViewFor < TViewModel > . ViewModel ) && view . ViewModel is not null )
114175 {
115176 view . SetupViewModel ( view . ViewModel ) ;
@@ -118,6 +179,11 @@ public virtual void PropertyChanged<TView>(TView view, string? propertyName = nu
118179
119180 public void OnLifecycle ( IStellarView < TViewModel > view , LifecycleEvent lifecycleEvent )
120181 {
182+ if ( _disposed )
183+ {
184+ return ; // Silently return if disposed to avoid exceptions during cleanup
185+ }
186+
121187 if ( view . ViewModel is ILifecycleEventAware lea )
122188 {
123189 lea . OnLifecycleEvent ( lifecycleEvent ) ;
@@ -133,6 +199,11 @@ public void OnLifecycle(IStellarView<TViewModel> view, LifecycleEvent lifecycleE
133199
134200 public void OnNavigating ( IStellarView < TViewModel > view , NavigationEvent navigationEvent )
135201 {
202+ if ( _disposed )
203+ {
204+ return ; // Silently return if disposed to avoid exceptions during cleanup
205+ }
206+
136207 if ( view . ViewModel is INavigationEventAware nea )
137208 {
138209 nea . OnNavigationEvent ( navigationEvent ) ;
0 commit comments