6

我们使用 Prism 启动了一个 WPF 项目,我遇到了一个问题:

有时在 ViewModel 中,我们注册一些事件或启动的服务,我们必须在关闭之前停止。这意味着当我关闭应用程序时,我需要释放我在 ViewModel 中获取的资源。然后 Dispose 会很有意义。

目前我正在使用ViewModelLocator.Autowire = TruePrism,我在想当不再需要 View 时,如果需要它会处理它。

我有两种情况:

  • 当我“导航”到视图时(RegionManager.RequestNavigate("RegionName", "RegionUri")
  • 当我在视图中使用“子视图”(这是具有自己的 ViewModel 的用户控件)时

我的问题是:处置这些 ViewModel 的正确方法是什么?我可以看到多种方法,但我不确定哪一种是正确的。

4

2 回答 2

3

由于您使用的是区域导航,因此我建议您使用简单的区域行为,该行为将在视图从区域中删除时调用您的界面方法。我在我的 Pluralsigh 课程中展示了一个例子: https ://www.pluralsight.com/courses/prism-problems-solutions

于 2016-06-09T08:58:07.290 回答
3

一般来说,您应该在 Unloaded 事件中进行清理登录。

我通过从 View 的 Loaded resp Unloaded 事件调用 ViewModel 上的 Activate 和 Deactivate 方法解决了这个问题。

interface IViewModelLifeCycle
{
   void Activate();
   void Deactivate();
}

public class MyComponentViewModel: BindableBase, IViewModelLifeCycle {
   public void Activate(){}
   public void Deactivate()
}

这与布莱恩的回答基本相同,我喜欢并赞成。这只是更通用,你不绑定到 RegionManager(我不喜欢 RegionManager)

[可选的]

为了让它更舒服,我创建了附加到视图的行为,而不是在代码后面编写一些代码:

<local:MyComponentView DataContext="{Binding MyComponentViewModel}"
                       local:ViewModelLifeCycleBehavior.ActivateOnLoad="True" />


<Style x:Key="PageStyle" TargetType="Page">
    <Setter Property="local:ViewModelLifeCycleBehavior.ActivateOnLoad" Value="True" />
</Style>

行为实现有点啰嗦,但实际上是非常简单的模式。在 PropertyChanged 回调中,附加到 FrameworkElements 事件。

public static class ViewModelLifeCycleBehavior
{
    public static readonly DependencyProperty ActivateOnLoadProperty = DependencyProperty.RegisterAttached("ActivateOnLoad", typeof (bool), typeof (ViewModelLifeCycleBehavior),
        new PropertyMetadata(ActivateOnLoadPropertyChanged));

    public static void SetActivateOnLoad(FrameworkElement element, bool value)
    {
        element.SetValue(ActivateOnLoadProperty, value);
    }

    public static bool GetActivateOnLoad(FrameworkElement element)
    {
        return (bool)element.GetValue(ActivateOnLoadProperty);
    }


    private static void ActivateOnLoadPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
    { 
        if (DesignerProperties.GetIsInDesignMode(obj)) return;
        var element = (FrameworkElement)obj;

        element.Loaded -= ElementLoaded;
        element.Unloaded -= ElementUnloaded;

        if ((bool) args.NewValue == true)
        {
            element.Loaded += ElementLoaded;
            element.Unloaded += ElementUnloaded;
        }
    }



    static void ElementLoaded(object sender, RoutedEventArgs e)
    {
        var element = (FrameworkElement) sender;
        var viewModel = (IViewModelLifeCycle) element.DataContext;
        if (viewModel == null)
        {
            DependencyPropertyChangedEventHandler dataContextChanged = null;
            dataContextChanged = (o, _e) =>
            {
                ElementLoaded(sender, e);
                element.DataContextChanged -= dataContextChanged;
            };
            element.DataContextChanged += dataContextChanged;
        }
        else if (element.ActualHeight > 0 && element.ActualWidth > 0) //to avoid activating twice since loaded event is called twice on TabItems' subtrees
        {
            viewModel.Activate(null);
        } 
    }

    private static void ElementUnloaded(object sender, RoutedEventArgs e)
    {
        var element = (FrameworkElement)sender;
        var viewModel = (IViewModelLifeCycle)element.DataContext;
        viewModel.Deactivate();
    }


}
于 2016-06-09T10:38:47.307 回答