Reputation: 466
(TLDR version is below.) I am designing a WPF application with MEF as the IoC and Caliburn Micro as the framework. The Main Window of the application is like this:
Here is the Viewmodel for the window:
[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);
}
So, _screenList
contains all the screens that needs to be displayed, and here's the MenuViewModel which publishes an event indicating the ViewModel to be displayed:
[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)));
}
But, the problem I am facing is that, instead of getting a new VM when I click a Button in the MenuViewModel
, I am getting a single instance again and again, which is actually expected, because this line finds the single instance of the ViewModel every time:
ActivateItem(_screenList.FirstOrDefault(c => c.GetType() == message.VMtype));
But I need to dispose of the ViewModel whenever a view is deactivated (I guess I have to do that using the OnDeactivate method of Screen class). But I don't know how to get a new instance of a ViewModel every time from _screenList
. My idea is to create some kind of factory, but I have no idea how to implement that using MEF, and how to dispose of a ViewModel whenever the view is deactivated.
SHORT VERSION: -TLDR-
In MEF, I can have something like this:
private IEnumerable<Screen> _screenList { get; set; }
[ImportingConstructor]
public MainViewModel(IEventAggregator eventAggregator, [ImportMany]IEnumerable<Screen> screenList)
{
_screenList = screenList;
}
This will import all components marked with [Export(typeof(Screen))]
attribute. However, each of those components are also marked with some other attribute, like [Export(typeof(ViewModelX))]
. Basically, Screen
is the base class from which each of the ViewModels are derived.
In my application, I am using _screenList
like so:
ActivateItem(_screenList.FirstOrDefault(c => c.GetType() == typeof(ViewModelX)));
However, in my problem, I want _screenList
to return a new instance of a ViewModelX
every time. How can I do that?
Upvotes: 1
Views: 323
Reputation: 466
Finally, found a solution to the problem. Using the ExportFactory
solved the problem.
The implementation is as follows:
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);
}
Upvotes: 1