Experiments using MEF, MVVM and Silverlight 4 Beta – Part 3: Part Creator and Creation Policy

Note – this is a multi part post:

In the last post I’ve inserted custom attributes in my MVVM implementation using MEF, in the meantime some awesome discussions and inputs on Twitter by Glenn Block have highlighted new interesting scenarios.

First of all, object instances are shared by default in MEF, however you can change this behaviour using a dedicated attribute named PartCreationPolicy during the export phase:

[PartCreationPolicy(CreationPolicy.NonShared)]
[ExportMainPageVMAttribute]
public class MainPageViewModel : ViewModelBase
{ .... class definition here .... }

In this way, all the ViewModel instances exported by MEF will be not shared and different views shall use different VM objects without any side-effect.

Another rule of thumb is avoiding calling the container directly in order to simplify architecture, as stated in this great article by Nicholas Blumhardt. This step requires the introduction of a new actor available in MEF: the declarative context adapter PartCreator<T>.

The first place in which I’ve used PartCreator<T> is in the code for the composition phase:

public MainPageView()
{
   InitializeComponent();

   PartInitializer.SatisfyImports(this);
   this.DataContext = mainPageViewModelCreator.CreatePart().ExportedValue;
}

[ImportMainPageVMAttribute]
public PartCreator<object> mainPageViewModelCreator { get; set; }

The VM instance is now retrieved calling the CreatePart() method of the PartCreator, instead of calling directly the container.

The same approach is now used to retrieve the instances relative to the aViewModelProperty and aSampleCommand:

[PartCreationPolicy(CreationPolicy.NonShared)]
[ExportMainPageVMAttribute]
public class MainPageViewModel : ViewModelBase
{
    /// <summary>
    /// Default constructor
    /// </summary>
    public MainPageViewModel() { }

    /// <summary>
    /// A Part Creator for the aViewModelProperty
    /// </summary>
    [Import("aViewModelPropertyTextProvider")]
    public PartCreator<string> aViewModelPropertyCreator { get; set; }

    /// <summary>
    /// A sample property
    /// </summary>
    private string _aViewModelProperty;
    public string aViewModelProperty
    {
        get
        {
            if (_aViewModelProperty == null)
            {
                _aViewModelProperty = aViewModelPropertyCreator.CreatePart().ExportedValue;
            }
            return _aViewModelProperty;
        }
        set { _aViewModelProperty = value; NotifyPropertyChanged("aViewModelProperty"); }
    }

    /// <summary>
    /// A Part creator for the sample command
    /// </summary>
    [Import(typeof(ICommand))]
    public PartCreator<ICommand> aSampleCommandCreator { get; set; }

    /// <summary>
    /// A sample command
    /// </summary>
    public ICommand aSampleCommand
    {
        get
        {
            return aSampleCommandCreator.CreatePart().ExportedValue;
        }
    }

    /// <summary>
    /// A simple property
    /// </summary>
    private string _aSimpleProperty = "A simple property sample";
    public string aSimpleProperty {
        get { return _aSimpleProperty; }
        set { _aSimpleProperty = value; NotifyPropertyChanged("aSimpleProperty"); }
    }
}

The VM instance is then created in xaml using a ViewModelPartCreator class:

/// <summary>
/// Get the ViewModel instance via the PartCreator<T>
/// </summary>
public class ViewModelPartCreator
{
    public ViewModelPartCreator() { }

    [ImportMainPageVMAttribute]
    public PartCreator<object> mainPageViewModelPartCreator { get; set; }

    /// <summary>
    /// Get the imported Instance of the ViewModel
    /// </summary>
    public object GetVMInstance
    {
        get
        {
            PartInitializer.SatisfyImports(this);
            return mainPageViewModelPartCreator.CreatePart().ExportedValue;
        }
    }
}

MainPageView.xaml:

<UserControl x:Class="SL4_MVVM_MEF.Views.MainPageView"
    .......
    xmlns:partCreators="clr-namespace:SL4_MVVM_MEF.PartCreators"
    >

    <UserControl.DataContext>
        <partCreators:ViewModelPartCreator/>
    </UserControl.DataContext>

    <StackPanel>
        <StackPanel Orientation="Horizontal">
            <TextBox Text="A ViewModel property MEF imported:" FontWeight="Bold"/>
            <TextBox Text="{Binding GetVMInstance.aViewModelProperty}" Margin="10,0,0,0"/>
        </StackPanel>
        <StackPanel Orientation="Horizontal">
            <TextBox Text="A ViewModel simple property:" FontWeight="Bold"/>
            <TextBox Text="{Binding GetVMInstance.aSimpleProperty}" Margin="10,0,0,0"/>
        </StackPanel>
        <Button x:Name="CommandButton" Command="{Binding GetVMInstance.aSampleCommand}" CommandParameter="This is a Command parameter"
                Content="Click me!" Margin="0,10" Width="200"/>
    </StackPanel>
</UserControl>

As usually the source code is available for download here.

A special thanks to Glenn Block for the awesome suggestions on Twitter :)

Happy Silverlighting!


9 Responses to “Experiments using MEF, MVVM and Silverlight 4 Beta – Part 3: Part Creator and Creation Policy”

Leave a Reply