OnInitialize, OnActivate and OnDeactivate not called when Conductor is implemented as a UserControl?

Topics: Framework Services, Getting Started
Jun 8, 2011 at 9:09 AM
Edited Jun 8, 2011 at 9:10 AM

I'm fairly new to Caliburn.Micro so I might be missing something here, but here goes...

I've been looking at AnsonSmith's Caliburn-Micro-Simple-Screen-Conductor-OneActive from github to get an idea of how I should use the Conductor functionality.

However, if I change the Bootstrapper.cs file to use my NewShellViewModel class insted of ShellViewModel and implement the NewShellViewModel class like this:

    public class NewShellViewModel : Screen
    {
        public ShellViewModel ShellViewModel
        {
            get { return new ShellViewModel(); }
        }
    }

and create a view, NewShellView, like this:

<Window x:Class="SimpleConductorOneActive.Views.NewShellView"
		  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
		  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
		  Title="MainWindow" Height="350" Width="550" Background="CornflowerBlue">
	 <Grid>
		<ContentControl x:Name="ShellViewModel"/>
	</Grid>
</Window>

and change this existing view ShellView to be a UserControl insted of a Window (and remote the Title-property from the XAML) then the ActiveItem-property of the ShellView is set correctly, but the OnInitialize, OnActivate and OnDeactivate on the RandomScreenViewModel class is never called?

What am I doing wrong here?

 

Jun 8, 2011 at 3:46 PM
Edited Jun 8, 2011 at 3:47 PM

when you start the app, do you see a small window with unable to locate a view or something along those lines?

Coordinator
Jun 8, 2011 at 4:29 PM
Edited Jun 8, 2011 at 4:31 PM

The problem you are having is that your ShellViewModel (which is itself a screen) has to be rooted either in another conductor or created by the window manager or bootstrapper. A screen *has to* be activated by a conductor or quasi-conductor (window manager & bootstrapper). Your screen is just a property, so it is not activated. You can activate it manually, or you can make your NewShellViewModel into a conductor and have it activate it. Have a read through of the docs on screens and conductors. It goes over this...it even calls it out specifically as a common error that people make. It's in the paragraph right before the section on quasi-conductors.

Screens and Conductors can be used to greatly improve/simplify your shell architecture. But if you are new to CM, it may not be the best starting point for you. It requires a bit of study to learn how to use them properly.

Jun 9, 2011 at 1:49 PM

Hi Rob, thanks for talking a look at my problem and helping me out with CM.

I had indeed read (and re-read) the Soup to Nuts Part 6a with the Conductor explanation, but apently I didn't understand it "the right way" - maybe it's because English isn't my native language?

I got is working, when I changed the NewShellViewModel class to this:

	public class NewShellViewModel : Conductor<Screen>
	{
		public NewShellViewModel()
		{
			ActivateItem(new ShellViewModel());
		}
	}

and changed this part of the NewShellView:

<Grid>
  <ContentControl x:Name="ShellViewModel"/>
</Grid>

to: 

<Grid>
  <ContentControl x:Name="ActiveItem"/>
</Grid>

So far so good! :)

Now let's say that ShellViewModel (not NewShellViewModel!) was implemented like this:

	[Export]
	public class ShellViewModel : Screen
	{
		[Import]
		public ChildViewModel ChildViewModel { getset; }
	}

then the ChildViewModel property is allway null, but if the ShellViewModel is a "simple property" like in my original post (i.e. not rooted in a contuctor), then MEF sets the proparty fine?
What am I doing wrong now? :)

Jun 9, 2011 at 2:18 PM

ShellViewModel  and ChildViewModel needs to be registered in the container and you shouln't do

ActivateItem( new ShellViewModel() );

Because then MEF will not build up the ShellViewModel and inject the dependencies for you. Use the ImportConstructor attribute and let MEF inject ShellViewModel for you

 

public class NewShellViewModel : Conductor<Screen>
	{
                [ImportingConstructor]
		public NewShellViewModel(ShellViewModel shell)
		{
			ActivateItem(shell);
		}
	}

Jun 9, 2011 at 2:31 PM

This looks like something to do with how you're using MEF. I'm not that familiar with it but from the look of the source you posted, you are expecting MEF to set the  ShellViewModel.ChildViewModel property for you (because of the [Import] attribute), but you are creating the ShellViewModel yourself so MEF doesn't get a chance to set it.

MEF cannot magically set properties on any object, it needs to either create those objects or be told to build up the properties on that object. Previously ShellViewModel was being created by MEF (via BootStrapper<T>.DisplayRootViewFor()) so MEF was setting that property.

There are various ways you could organize your view models to allow this but the simplest/quickest thing to do that might make it work is to ask MEF to create your ShellViewModel by replacing new ShellViewModel() with IoC.GetInstance<ShellViewModel>().

Jun 9, 2011 at 3:45 PM

@syggen + @leafgarland: Thanks - makes sense (and works) now!