My app uses views, which implement IViewFor<T>
interface. The views are registered with the dependency resolver in AppBootstrapper
. The app displays the views using ViewModelViewHost
control by assigning a corresponding view model to control's ViewModel
property. All the view models implement ISupportsActivation
interface.
I noticed that WhenActivated
is called twice. First it's called when a view and view model get activated. Then on deactivation all disposables are disposed and WhenActivated
is called again immediately followed by disposing the disposables.
I am testing with the following code both in view and view model:
this.WhenActivated(disposables =>
{
Debug.WriteLine("ViewModel activated.");
Disposable
.Create(() =>
{
Debug.WriteLine("ViewModel deactivated.");
})
.AddTo(disposables);
});
As a result the output looks like this:
// App displays the view:
ViewModel activated.
View activated.
// App hides the view:
ViewModel deactivated.
View deactivated.
ViewModel activated.
View activated.
ViewModel deactivated.
View deactivated.
The view is hidden by setting ViewModel property of ViewModelViewHost control to null.
Am I doing something wrong?
Edit: here's the complete source code: https://gist.github.com/dmakaroff/e7d84e06e0a48d7f5298eb6b7d6187d0
Pressing first Show and then Hide buttons produces the following output:
SubViewModel activated.
SubView activated.
SubViewModel deactivated.
SubView deactivated.
SubViewModel activated.
SubView activated.
SubViewModel deactivated.
SubView deactivated.
The WhenActivated
call used in SubView
returns an IDisposable
object, which can be used within the same call to WhenActivated.
This will remove your subscription from activation events upon deactivation. Doing so prevents the secondary activation and disposal from occurring.
In the SubView
constructor, change this:
this.WhenActivated(d =>
{
Debug.WriteLine("SubView activated.");
d(Disposable.Create(() => { Debug.WriteLine("SubView deactivated."); }));
d(this // ViewModel -> DataContext
.WhenAnyValue(v => v.ViewModel)
.BindTo(this, v => v.DataContext));
});
to this:
System.IDisposable whenActivatedSubscription = null;
whenActivatedSubscription = this.WhenActivated(d =>
{
Debug.WriteLine("SubView activated.");
d(Disposable.Create(() => { Debug.WriteLine("SubView deactivated."); }));
d(this // ViewModel -> DataContext
.WhenAnyValue(v => v.ViewModel)
.BindTo(this, v => v.DataContext));
d(whenActivatedSubscription); // <- Dispose of the activation subscription here
});
The reason why this solution works is because since your view is being destroyed, the activation itself needs to be disposed of as a part of this process as well.