I'm experimenting with using Caliburn and MEF in a WPF application. My MEF knowledge is sketchy at best.
Here's the Bootstrapper:
class Bootstrapper : BootstrapperBase
{
public Bootstrapper()
{
Initialize();
}
protected override void OnStartup(object sender, StartupEventArgs e)
{
DisplayRootViewFor<IShell>();
}
protected override void Configure()
{
//An aggregate catalog that combines multiple catalogs
var catalog = new AggregateCatalog();
//Adds all the parts found in the same assembly as the Program class
catalog.Catalogs.Add(new AssemblyCatalog(typeof(Bootstrapper).Assembly));
//Create the CompositionContainer with the parts in the catalog
container = new CompositionContainer(catalog);
var batch = new CompositionBatch();
batch.AddExportedValue<IWindowManager>(new WindowManager());
batch.AddExportedValue<IEventAggregator>(new EventAggregator());
batch.AddExportedValue(container);
container.Compose(batch);
}
protected override object GetInstance(Type serviceType, string key)
{
string contract = string.IsNullOrEmpty(key) ? AttributedModelServices.GetContractName(serviceType) : key;
var exports = container.GetExportedValues<object>(contract);
if (exports.Any())
return exports.First();
throw new Exception(string.Format("Could not locate any instances of contract {0}.", contract));
}
protected override IEnumerable<object> GetAllInstances(Type serviceType)
{
return container.GetExportedValues<object>(AttributedModelServices.GetContractName(serviceType));
}
protected override void BuildUp(object instance)
{
container.SatisfyImportsOnce(instance);
}
}
Here's the shellview:
<Window x:Class="MefCaliburn.ShellView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:MefCaliburn"
mc:Ignorable="d"
Title="ShellView" Height="300" Width="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="20"/>
<RowDefinition/>
<RowDefinition Height="100"/>
</Grid.RowDefinitions>
<ContentControl Grid.Row="0" x:Name="Menu"></ContentControl>
<ContentControl Grid.Row="1"></ContentControl>
<ContentControl Grid.Row="2" x:Name="Messages"></ContentControl>
</Grid>
The IShell interface:
public interface IShell
{
}
Here's my shell view model:
namespace MefCaliburn
{
[Export(typeof(IShell))]
public class ShellViewModel : PropertyChangedBase, IShell
{
private readonly IEventAggregator _events;
UserViewModel uvm;
[ImportingConstructor]
public ShellViewModel(MenuViewModel menu, MessagesViewModel mess, IEventAggregator eventaggregator)
{
Messages = mess;
Menu = menu;
_events = eventaggregator;
}
public MessagesViewModel Messages
{
get; set;
}
public MenuViewModel Menu
{
get; set;
}
public void LaunchUserViewModel()
{
uvm = new UserViewModel();
}
}
}
So when the Boostrapper overridden method
protected override void OnStartup(object sender, StartupEventArgs e)
{
DisplayRootViewFor<IShell>();
}
gets called, my ShellViewModel constructor is called and the Menu and Messages view models are Injected. This is an example of dependency injection, correct?
In my case the Menu & Messages view models are created along with the shell. But if a new view model
[Export(typeof(UserViewModel))]
public class UserViewModel : PropertyChangedBase
{
private readonly IEventAggregator _events;
[ImportingConstructor]
public UserViewModel(IEventAggregator eventaggregator)
{
_events = eventaggregator;
}
}
is created when the user presses a button on the MenuView.xaml
<UserControl x:Class="MefCaliburn.MenuView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:MefCaliburn"
xmlns:cal="http://www.caliburnproject.org"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<StackPanel Orientation="Horizontal">
<Button Content="ONE" cal:Message.Attach="[Event Click] = [Action LaunchUserViewModel()]"></Button>
<Button Content="TWO"></Button>
</StackPanel>
</Grid>
This UserViewModel will be publishing events to the MessagesViewModel, I'm going to need the IEventAggregator. Yes, I can just pass it in explicitly to the constructor, but what I'm trying to understand is why MEF won't inject it.
Am I trying to use MEF in a manner it's not intended? Is MEF only meant to be used when the application starts up for those view models that are known to be required?
IEventAggregatoras an exported value? Something like this I mean:AddExportedValue<IEventAggregator>(new EventAggregator());- FCinUserViewModelis also exported. You have to export it to be able to import values into it. Also don't forget to rebuild your solution. - FCin