I have two applications, one is the main application and the other is a designer form application.
At the moment I have my main application working with prism and mef. Some of the Views in my main application are just data entry forms. What I want from my designer form application is to load up a data entry form view so it can then be edited, but to do this I want to use a different viewmodel for designing purposes. I don't want the form to attach to it's usual viewmodel and try to get data etc.
How using MEF would I be able to provide a different export so that it picks this one up instead of the usual viewmodel? Ideally it would just replace the main application viewmodel, so it just uses that instead.
This is my example view, importing a viewmodel
[Export("PatientDetailView")]
[PartCreationPolicy(CreationPolicy.NonShared)]
public partial class PatientDetailView : UserControl
{
[ImportingConstructor]
public PatientDetailView(PatientDetailViewModel viewModel)
{
InitializeComponent();
this.DataContext = viewModel;
}
}
And here is the basis for my viewmodel:
[Export(typeof(PatientDetailViewModel))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class PatientDetailViewModel : ViewModelBase, IRegionManagerAware
{
[ImportingConstructor]
public PatientDetailViewModel(IEventAggregator eventAggregator, IDialogService dialogService, IRegionManager regionManager)
: base(eventAggregator, dialogService, regionManager)
{
//Contains Commands etc for Saving Patient Detail Record
//Receiving patient detail etc
}
}
UPDATE:
The above is contained in a patient module assembly. This works how it should for the main application. For the Designer application I want to replace the above view model with something like the below:
[Export(typeof(PatientDetailViewModel))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class PatientDetailViewModel : ViewModelBase, IRegionManagerAware
{
[ImportingConstructor]
public PatientDetailViewModel(IEventAggregator eventAggregator, IDialogService dialogService, IRegionManager regionManager)
: base(eventAggregator, dialogService, regionManager)
{
//Contains Commands etc for Designing the form
//No commands from the original VM so changes how it tries to work.
}
}
The above is working to override the default behaviour for my main application. This VM will be contained in the Designer assembly or a separate designerVMs assembly.
since these are seperate applications, and a datacontext can be any object, the solution can be simple.
The view is changed to import it's datacontext by name.
public static class MefContracts
{
public const string PatientDetailViewModel = "PatientDetailViewModel";
}
[Export("PatientDetailView")]
[PartCreationPolicy(CreationPolicy.NonShared)]
public partial class PatientDetailView : UserControl, IPartImportsSatisfiedNotification
{
[Import( MefContracts.PatientDetailViewModel, typeof( object )]
private object vm;
public void OnImportsSatisfied()
{
this.DataContext = vm;
}
public PatientDetailView()
{
InitializeComponent();
}
}
Then depending on the application you only include the ViewModel you want and export it by name
[Export( MefContracts.PatientDetailViewModel, typeof( object ) ) )]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class PatientDetailViewModel : ViewModelBase, IRegionManagerAware
{
....
}
update if you really cannot seperate assemblies (which is imo still the best option, there is nothing wrong with having the vm in a different assembly), you could work with a simple factory instead (to abstract the way a ViewModel is acquired): instead of importing the vm, you import a factory that can create the vm. Which one it creates is up to some configuration value that has to be set differently in each app. You will have to export the vms with a different contract then, else there is no (easy) way to differentiate between them. Example:
public static class MefContracts
{
public const string PatientDetailViewModelMain = "PatientDetailViewModelMain";
public const string PatientDetailViewModelDesigner = "PatientDetailViewModelDesigner";
}
//the main vm
[Export( MefContracts.PatientDetailViewModelMain, typeof( object ) ) )]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class PatientDetailViewModel : ViewModelBase, IRegionManagerAware
{
....
}
//the other vm
[Export( MefContracts.PatientDetailViewModelDesigner, typeof( object ) ) )]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class OtherPatientDetailViewModel : ViewModelBase, IRegionManagerAware
{
....
}
[Export("PatientDetailView")]
[PartCreationPolicy(CreationPolicy.NonShared)]
public partial class PatientDetailView : UserControl
{
[ImportingConstructor]
public PatientDetailView( PatientDetailViewModelFactory viewModelFactory )
{
InitializeComponent();
this.DataContext = viewModelFactory.Create();
}
}
[Export]
class PatientDetailViewModelFactory
{
[Import]
private CompositionContainer container{ get; set; }
public enum AppType{ Main, Designer }
public AppType AppType{ get; set; }
public object Create()
{
return container.GetExportedValue<object>(
AppType == AppType.Main ? MefContracts.PatientDetailViewModelMain :
MefContracts.PatientDetailViewModelDesigner );
}
}