Layout composition / navigation with Caliburn.Micro

793 Views Asked by At

So, I'm looking forward to create a simple layout in Caliburn.Micro, but I'm a little confused about how things are intended to be done.

The target layout looks something like this:

_______________________________________
|           |                          |
|           |                          |
|           |                          |
|    NAV    |          MAIN            |
|           |                          |
|           |                          |
|           |                          |
|___________|__________________________|

From what I'm reading on the many tutorials, there should be a root view model usually called Shell which derives from a Conductor and activates the Main view using ActivateItem(). So far, so good. But what if the Nav is itself a view, not just some stack panel with buttons? I tried the following:

## ShellViewModel
public class ShellViewModel : Conductor<IScreen>, IShell {

    private IScreen _navigationScreen;

    public IScreen NavigationScreen {
        get { return _navigationScreen ?? (_navigationScreen = new NavigationViewModel()); }
    }

}

## ShellView
<Window...
    <ContentControl x:Name="NavigationScreen" />
    <ContentControl x:Name="ActiveItem />
</Window>

The navigation view gets loaded perfectly fine. But now I've got a few questions:

  • How am I supposed to signal e.g. the click on a button up to the ShellViewModel so it can change the ActiveItem? Through the EventAggregator (really, broadcasting an event for all interested handlers just for a button?)?

  • Why does the view model (Shell) need to care about the layout? Wouldn't it be better to have some kind of router for composition/layout stuff?

  • What about dependency injection? Let's say the NavigationViewModel needs a file reader because it loads (I know, stupid example) it's navigational items out of a file? Since I'm creating it myself in the Shell, I need to have a reference to that dependency where it doesn't even belong. Or is the Shell some kind of god class that holds reference to every dependency just to pass them to the independent views?

I'm unfortunately unable to find any tutorials on Caliburn that go further than just baby steps and simple view composition, but from what I figured out so far, it all seems tightly coupled with no respect for DI etc. Or am I missing something?

1

There are 1 best solutions below

2
On

DI is very available of course it is dependent on your container of choice, MEF, most samples feature it, SimpleContainer (baked in). There are Bootstrappers implimentations that are adapted for all other containers out there (StructureMap, Windsor, Ninject, etc), built in there is a static IoC class for getting to registered objects in the container outside of the bootstrapper, but I feel and some will say this as well that it has anti-pattern tendencies using it too much, use only when absolutely necessary.

To tell the views to switch for current actives, even though the sample is based on "dialogs"; Hello Screens does show how this is done cross viewmodel (ie shell to workspace) it is probably best answer to that bullet point

As for composition some will go with a custom conductor depending on the complexity you want. I tend to go with KISS method and only separate out in to modules when I feel it's worth it. EventAggregator is very handy for cross viewmodel communications. if you intend to use composition of course Conductor<> is a good choice, view switching can also be implemented or a combination of Conductor and multi-view single viewmodel designs.

public class ShellViewModel : Conductor<IScreen>, IShell
{ 
   //INavigationViewModel is a "service interface" to the actual implimentation
   private INavigationViewModel _navmodel
   public class ShellViewModel(INavigationViewModel navmodel){

       _navmodel = navmodel;   

   }
} 

//can be done as long as NavigationViewModel is in the container of your choosing

Inside your NavigationViewModel you would probably work with something like IConductor interface that would tell the Conductor on the Shell that there is a new active item to process..

Just keep in mind that CM is viewmodel first primarily but does have the ability to do view first and is all in the configuration.. Choose one don't mix as a suggestion, mixing leads to weird behavior.

HTHs