WhenActivated 在 ViewModelViewHost 控件中托管的 Views 和 ViewModel 中使用时被调用两次

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

我的应用程序使用实现 IViewFor<T> 接口的视图。这些视图在 AppBootstrapper 中注册到依赖项解析器。该应用通过将相应的视图模型分配给控件的 ViewModel 属性,使用 ViewModelViewHost 控件显示视图。所有视图模型都实现了 ISupportsActivation 接口。

我注意到 WhenActivated 被调用了两次。首先,当视图和视图模型被激活时它被调用。然后在停用时处理所有一次性用品并立即再次调用 WhenActivated 然后处理一次性用品。

我正在视图和视图模型中使用以下代码进行测试:

this.WhenActivated(disposables =>
{
    Debug.WriteLine("ViewModel activated.");

    Disposable
        .Create(() =>
        {
            Debug.WriteLine("ViewModel deactivated.");
        })
        .AddTo(disposables);
});

结果输出如下所示:

// App displays the view:

ViewModel activated.
View activated.

// App hides the view:

ViewModel deactivated.
View deactivated.
ViewModel activated.
View activated.
ViewModel deactivated.
View deactivated.

通过将 ViewModelViewHost 控件的 ViewModel 属性 设置为空来隐藏视图。

我是不是做错了什么?

编辑: 这是完整的源代码:https://gist.github.com/dmakaroff/e7d84e06e0a48d7f5298eb6b7d6187d0

先按显示然后按隐藏按钮会产生以下输出:

SubViewModel activated.
SubView activated.
SubViewModel deactivated.
SubView deactivated.
SubViewModel activated.
SubView activated.
SubViewModel deactivated.
SubView deactivated.

您可以查看this link。一切都有很好的解释。这就是我在不了解您在“视图”中所做的事情的情况下所能提供的全部内容。

“ViewModel 和 View”可能被调用了两次,因为您正在创建两个 LoadingViewModel。

WhenActivated 调用在 SubView returns 一个 IDisposable 对象中使用,可以在对 WhenActivated. 的同一调用中使用这将删除您的停用时从激活事件订阅。这样做可以防止二次激活和处置的发生。

SubView 构造函数中,更改为:

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));
});

对此:

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
});

此解决方案之所以有效,是因为您的视图正在被销毁,激活本身也需要作为此过程的一部分进行处理。