IContextAware.InjectContext() isn't called when re-using a shared ViewModel

Jun 27, 2012 at 4:58 PM
Edited Jun 27, 2012 at 5:02 PM

I've been playing with the IContextAware services (and mefedMVVM in general) to get a feel for a major refactoring project. In my test code I'm using shared ViewModels and the NavigationExtensions. So a new View is created each time the usercontrol is navigated too while the ViewModel will (likely) persist in memory from the moment it's constructed (it's a design constraint BTW).

My issue is I don't receive a load/unload event from the IContainerStatus service after it's requested by a second view (having navigated away & back), nor is the InjectContext Method called when I add a breakpoint to my test IContextAware service.

MEF is presumably ignoring the need for a re-import as a non-shared class is being imported by my shared view-model, however I see that the defaultContainerStatus service supports the context changing after it's initialised but I can't see that the context is set outside the GetExportsCore override in the ExportProvider (admitedly knowing as little as I can get away with about the plumbing in MEF).

Are Context services supported in shared view-models?

NuGet tells me I'm using v1.4, however I compiled the NavigationExtensions myself from version 83479 (couldn't find a binary).. see code below:

View Model:

[PartCreationPolicy(System.ComponentModel.Composition.CreationPolicy.Shared ) ]
[ExportViewModel("SecondScreenViewModel")]
class SecondScreenViewModel : IPartImportsSatisfiedNotificationINotifyPropertyChanged
{
	// services
	[Importpublic IContainerStatus ViewState { getset; }
	[Importpublic ITestService TestContextService { getset; }
 
 
	// properties
	public string Name { getset; }
 
 
	// constructor
	public SecondScreenViewModel()
	{
		Name = "Hello, World!";
	}
 
 
	// Imports loaded
	public void OnImportsSatisfied()
	{
		// re-subscribe handled in service..
		ViewState.ContainerLoaded += new Action( ViewLoadedHandler );
		ViewState.ContainerUnloaded += new Action( ViewClosedHandler );
	}
 
 
	// event handlers
	void ViewLoadedHandler()
	{
		Name = Name + " Loaded";
		RaisePropertyChanged( "Name" );
	}
 
	int viewDisposalCount = 1;
	void ViewClosedHandler()
	{
		Name = "View Unloaded " + ( viewDisposalCount++ ) + " times.";
		RaisePropertyChanged( "Name" );
	}
 
 
	// INotifyPropertyChanged
	public event PropertyChangedEventHandler PropertyChanged;
	protected void RaisePropertyChanged( string name )
	{
		PropertyChangedEventHandler handler = PropertyChanged;
		if ( handler != null ) handler( thisnew PropertyChangedEventArgs( name ) );
	}
}

Test services:

interface ITestService : IContextAware
{
	object Context { getset; }
}

[PartCreationPolicy( System.ComponentModel.Composition.CreationPolicy.NonShared )] [ExportServiceServiceType.Both, typeofITestService ) )] class TestService : ITestService { public TestService() { Context = new object(); } public object Context { getset; } public void InjectContext( object context ) { Context = context; } }

View Code:

[NavigationView("NSL.Leisure.Reception.NewArchitecture.Views.TestViews.SecondScreenView" )]
public partial class SecondScreenView : UserControlIPartImportsSatisfiedNotification
{
	public SecondScreenView()
	{
		InitializeComponent();
	}
 
	public void OnImportsSatisfied()
	{
		var VM = this.DataContext as NSL.Leisure.Reception.NewArchitecture.ViewModels.TestViews.SecondScreenViewModel;
 
		if ( VM != null && VM.TestContextService.Context == this )
		{
			Debug.WriteLine( "Import successful" );
		}
		else
		{
			Debug.WriteLine( "Didn't inject context" );
		}
	}
}

View XAML:

<Grid>		
	<StackPanel>
		<TextBlock Margin="10" Text="{Binding Name, StringFormat='Bound data: {0}', TargetNullValue='null', FallbackValue='null'}" />
		<Button HorizontalAlignment="Center"
			    Content="GOTO: Screen 1"
			    mefed:NavigationExtensions.NavigateTo="NSL.Leisure.Reception.NewArchitecture.Views.TestViews.FirstScreenView"
			    mefed:NavigationExtensions.NavigationHost="{util:NamedContentHost MainContentHost}" />
	</StackPanel>
 
	<!--  screen 2  -->
	<TextBlock HorizontalAlignment="Center"
		        VerticalAlignment="Center"
		        FontSize="40"
		        Foreground="#C0C0C0"
		        Text="2" />
	<TextBlock HorizontalAlignment="Center"
		        VerticalAlignment="Center"
		        Foreground="ForestGreen"
		        Text="/* Second Screen */" />
</Grid>