Autofac:将LifeTimeScope与MVVM中ViewModel的生命周期联系起来

时间:2016-05-31 15:11:40

标签: wpf mvvm autofac

我很惊讶每个IoC / MVVM教程都没有涵盖这个问题,所以希望我遗漏了一些明显的问题。

简而言之,我的问题是:如何将Autofac LifeTimeScope与ViewModel的生命周期联系起来。

考虑下面的简单应用程序。

-------------------------------------------------
|                                          _ o x |
-------------------------------------------------
|          |                                     |
| Item A   |                                     |
| Item B   |       DetailView for Item A         |
| Item C   |                                     |
|          |                                     | 
|------------------------------------------------

此应用程序包含五个视图:

  • 的MainView
  • 的ListView
  • ADetailView
  • BDetailView
  • CDetailView

以及五个视图模型

  • MainViewModel
  • ListViewModel
  • ADetalViewModel
  • BDetalViewModel
  • CDetalViewModel

主视图的结构类似于此。

<Grid Margin="5">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="1*"></ColumnDefinition>
        <ColumnDefinition Width="5"></ColumnDefinition>
        <ColumnDefinition Width="2*"></ColumnDefinition>
    </Grid.ColumnDefinitions>

    <ListView Grid.Column="0">
    </ListView>

    <GridSplitter Grid.Column="1"/>

    <Border Grid.Column="2">
        <Border.Resources>
            <DataTemplate DataType="{x:Type vm:ADetailViewModel}">
                <vw:ADetailView></vw:ADetailView>
            </DataTemplate>
            <DataTemplate DataType="{x:Type vm:BDetailViewModel}">
                <vw:BDetailView></vw:BDetailView>
            </DataTemplate>
            <DataTemplate DataType="{x:Type vm:CDetailViewModel}">
                <vw:CDetailView></vw:CDetailView>
            </DataTemplate>
        </Border.Resources>
        <ContentPresenter Content="{Binding CurrentDetailViewModel}"></ContentPresenter>
    </Border>

</Grid>

因此它是一个模板驱动器用户界面,其中模板类型将触发更改正确的视图。为此,我们必须实例化一个正确类型的ViewModel,并将其分配给MainViewModel中的CurrentDetailViewModel。

现在我到了这个问题。我非常喜欢创建这个细节视图模型来触发LifetimeScope的创建。然后,它的所有依赖项都将驻留在此范围内,并且在切换到另一个视图/视图模型时可以轻松处理。

这对于Autofac来说是一个非常常见的用例,我很好奇其他人如何接近它。

1 个答案:

答案 0 :(得分:0)

我会将当前的方法添加到答案列表中,希望有人会添加或给出更好的答案。

我利用了Owned。当容器找到声明为Owned<>的东西时,它将在自己的新生命周期中创建实例。然后,任何依赖项(默认情况下)也会在此lifetimescope中结束。必须通过调用Dispose()手动销毁生命时间镜。在下面的示例中,我将Owned与Func结合起来,以获得一个工厂来创建任意数量的拥有实例。

构造

private readonly Func<Owned<ADetailViewModel> _aFactory;
private readonly Func<Owned<BDetailViewModel> _bFactory;
private readonly Func<Owned<CDetailViewModel> _cFactory;

private IDisposable _currentOwned;

public MainViewModel(Func<int, Owned<ADetailViewModel>> aFactory,
                     Func<int, Owned<BDetailViewModel>> bFactory,
                     Func<int, Owned<CDetailViewModel>> cFactory)
{
    _aFactory= aFactory;
    _bFactory= bFactory;
    _cFactory= cFactory;
}

SwitchDetailViewCommand:

private RelayCommand<IListItemViewModel> _switchDetailViewCommand ;
public RelayCommand<IListItemViewModel> SwitchDetailViewCommand 
{
    get
    {
        return _switchDetailViewCommand ?? (_switchDetailViewCommand = new RelayCommand<IListItemViewModel>(
            (listitem) =>
            {   
                if (_currentOwned != null)
                {
                    _currentOwned.Dispose();
                }   
                switch (listitem.Type)
                {
                    case "A":
                    {
                        var aOwned = _aFactory();
                        _currentOwned = aOwned;
                        CurrentDetailViewModel = _aOwned.Value;
                        break;
                    }
                    case "B":
                    {
                        var bOwned = _bFactory();
                        _currentOwned = bOwned;
                        CurrentDetailViewModel = _bOwned.Value;
                        break;
                    }
                    case "C":
                    {
                        var cOwned = _cFactory();
                        _currentOwned = cOwned;
                        CurrentDetailViewModel = _cOwned.Value;
                        break;
                    }
                    default:
                    {
                        break;
                    }
                }
            }
        ));
    }
}