Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WhenActivated is called twice when used in Views and ViewModels hosted in ViewModelViewHost control

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.
like image 308
dimiboi Avatar asked Apr 05 '16 17:04

dimiboi


1 Answers

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.

like image 129
cokeman19 Avatar answered Sep 25 '22 02:09

cokeman19