处理ViewModels的模式?

时间:2016-06-09 07:16:22

标签: c# wpf mvvm prism prism-6

我们用Prism开始了一个WPF项目,我提出了一个问题:

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

目前我正在使用Prism的ViewModelLocator.Autowire = True,我当时认为当不再需要View时,如果需要它会处理它。

我有两个案例:

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

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

2 个答案:

答案 0 :(得分:2)

我很一般,你应该在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()
}

这基本上与Brian's answer中的原则相同,我喜欢和赞成。这只是更通用的,你不依赖于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();
    }


}

答案 1 :(得分:1)

由于您正在使用区域导航,因此我建议您使用一种简单的区域行为,只要从区域中删除视图,就会调用您的接口方法。我在Pluralsigh课程中展示了这个例子:https://www.pluralsight.com/courses/prism-problems-solutions