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

2

8

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.
Ohmmeter answered 5/4, 2016 at 17:59 Comment(3)
I'm not having any luck reproducing the issue. Could you post the relevant parts of your AppBootstrapper, view and view-model classes (and any other classes in play)?Cynde
@Cynde Here is the complete source code: gist.github.com/pupunussi/e7d84e06e0a48d7f5298eb6b7d6187d0Ohmmeter
gist.github.com/dmakaroff/e7d84e06e0a48d7f5298eb6b7d6187d0Ohmmeter
C
11

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.

Cynde answered 23/4, 2016 at 16:23 Comment(2)
Thanks a lot! This solves the problem. Documentation says nothing about disposing of activation subscription.Ohmmeter
Woah, why isn't this documented? Thank you very much.Andreaandreana
U
-1

You can check this link. There is a very good explenation of everything. This is all I can offer without the knowledge of what you did in the "View" itself.

It is possible that the "ViewModel and View" are called twice because you are creating two LoadingViewModels.

Ursas answered 19/4, 2016 at 6:18 Comment(2)
Link is broken.Rexford
Perhaps this link?Righthander

© 2022 - 2024 — McMap. All rights reserved.