将MEF与Caliburn Micro的屏蔽导体配合使用

时间:2017-05-20 14:45:41

标签: c# wpf mef caliburn.micro factory-pattern

(TLDR版本如下。)我正在设计一个WPF应用程序,MEF作为IoC和Caliburn Micro作为框架。应用程序的主窗口是这样的:

Main Window

以下是窗口的Viewmodel:

[Export(typeof(MainViewModel))]
class MainViewModel : Conductor<PropertyChangedBase>, IHandle<ViewModelType>
{
    private readonly IEventAggregator _eventAggregator;

    private IEnumerable<Screen> _screenList { get; set; }

    [ImportingConstructor]
    public MainViewModel(IEventAggregator eventAggregator, [ImportMany]IEnumerable<Screen> screenList)
    {
        _screenList = screenList;
        _eventAggregator = eventAggregator;                    
        _eventAggregator.Subscribe(this);
        ShowMenu();
    }

    public void Handle(ViewModelType message)
    {           
        ActivateItem(_screenList.FirstOrDefault(c => c.GetType() == message.VMtype));
        DisplayName = "B.I. Surgical & Dressing - " + (ActiveItem as Screen)?.DisplayName;
        NotifyOfPropertyChange(() => CanShowMenu);
    }

    public void ShowMenu() => _eventAggregator.PublishOnUIThread(new ViewModelType(typeof(Menu.MenuViewModel)));
    public bool CanShowMenu => ActiveItem.GetType() != typeof(Menu.MenuViewModel);
}

因此,_screenList包含需要显示的所有屏幕,这里是MenuViewModel,它发布一个事件,指示要显示的ViewModel:

[Export(typeof(MenuViewModel))]
[PartCreationPolicy(CreationPolicy.NonShared), Export(typeof(Screen))]
class MenuViewModel : Screen
{
    private readonly IEventAggregator _eventAggregator;        

    [ImportingConstructor]
    public MenuViewModel(IEventAggregator eventAggregator)
    {
        _eventAggregator = eventAggregator;
        DisplayName = "Menu";
    }

    public void CreateInvoice() => _eventAggregator.PublishOnUIThread(new ViewModelType(typeof(Invoice.InvoiceViewModel)));
    public void EnterPaymentsReceived() => _eventAggregator.PublishOnUIThread(new ViewModelType(typeof(ReceivedPayments.PaymentsReceivedViewModel)));
    public void EnterPurchases() => _eventAggregator.PublishOnUIThread(new ViewModelType(typeof(PurchaseDetails.PurchaseDetailsViewModel)));
    public void AddClientAndRates() => _eventAggregator.PublishOnUIThread(new ViewModelType(typeof(AddClient.AddClientViewModel)));
    public void EditClientAndRates() => _eventAggregator.PublishOnUIThread(new ViewModelType(typeof(EditClient.EditClientViewModel)));
    public void AddItem() => _eventAggregator.PublishOnUIThread(new ViewModelType(typeof(AddItem.AddItemViewModel)));
    public void EditItems() => _eventAggregator.PublishOnUIThread(new ViewModelType(typeof(EditItem.EditItemViewModel)));

}

但是,我面临的问题是,当我点击MenuViewModel中的一个按钮时,我不是一个新的虚拟机,而是一次又一次地获得一个实例,这实际上是预期的,因为这一行每次都找到ViewModel的单个实例:  ActivateItem(_screenList.FirstOrDefault(c => c.GetType() == message.VMtype));

但是每当视图被停用时我都需要处理ViewModel(我想我必须使用Screen类的OnDeactivate方法)。但我不知道每次从_screenList获取ViewModel的新实例。我的想法是创建某种工厂,但我不知道如何使用MEF实现它,以及如何在视图被停用时处理ViewModel。

简短版: -TLDR -

在MEF中,我可以这样:

private IEnumerable<Screen> _screenList { get; set; }

[ImportingConstructor]
public MainViewModel(IEventAggregator eventAggregator, [ImportMany]IEnumerable<Screen> screenList)
{
    _screenList = screenList;
}

这将导入标有[Export(typeof(Screen))]属性的所有组件。但是,每个组件也都标有其他一些属性,例如[Export(typeof(ViewModelX))]。基本上,Screen是从中派生每个ViewModel的基类。

在我的应用程序中,我正在使用_screenList,如下所示: ActivateItem(_screenList.FirstOrDefault(c => c.GetType() == typeof(ViewModelX)));

但是,在我的问题中,我希望_screenList每次都返回ViewModelX的新实例。我怎么能这样做?

1 个答案:

答案 0 :(得分:1)

最后,找到了问题的解决方案。使用ExportFactory解决了问题。

实施如下:

private IEnumerable<ExportFactory<Screen>> _screenList { get; set; }

[ImportingConstructor]
public MainViewModel(IEventAggregator eventAggregator, [ImportMany] IEnumerable<ExportFactory<Screen>> screenList)
{            
    _screenList = screenList;
    _eventAggregator = eventAggregator;                    
    _eventAggregator.Subscribe(this);
    ShowMenu();
}

public void Handle(ViewModelType message)
{
    ActivateItem(_screenList.FirstOrDefault(c => c.CreateExport().Value.GetType() == message.VMtype).CreateExport().Value);
}