WPF Caliburn.Micro/mvvm Navigation
Asked Answered
M

2

7

I'm building a project, and one of the biggest problems I've come across until now is navigation.
I've been looking for some time now for examples of caliburn.micro/mvvm navigation, but they all seem to be really long and I couldn't really understand much of it (beginner here!).

Some info about my project:
I want there to be an outer window/shell, with menu links/tabs that open pages according to the button clicked inside an inner part of the shell, and be able to open change the page from within a one.

I currently have: ShellViewModel.cs, MainViewModel.cs, my models, and my views. For now, all I need to know is how to make MainViewModel load inside shellviewmodel on startup(using contentcontrol/frames...), and how to move from one page to another.

You could also just write it in points, and link me to some useful examples, and I believe I could continue from there. It'd be best to get a thorough explanation of stuff if possible.

Marriage answered 14/5, 2013 at 14:22 Comment(0)
S
10

Have a read about Conductors and Screens on the official documentation.

As a simple example, your ShellViewModel could be a Conductor of one active screen (i.e. only one screen becomes active/inactive at a time):

public class ShellViewModel : Conductor<IScreen>.Collection.OneActive

You can then set the ActiveItem of the Conductor to the view model instance that you wish to be currently active:

this.ActivateItem(myMainViewModel);

A collection Conductor type also provides an Items collection which you can populate as you instantiate new windows. Viewmodels in this Items collection may be those that are currently deactivated but not yet closed, and you can activate them by using ActivateItem as above. It also makes it very easy to create a menu of open windows by using an ItemsControl with x:Name="Items" in your ShellView.

Then, to create the ShellView, you can use a ContentControl and set its name to be the same as the ActiveItem property, and Caliburn.Micro will do the rest:

<ContentControl x:Name="ActiveItem" />

You can then respond to activation/deactivation in your MainViewModel by overriding OnActivate/OnDeactivate in that class.

Serpentine answered 14/5, 2013 at 14:26 Comment(6)
Well, you definitely simplified the idea! Is a collection Conductor type something I should use when I want to add "backward"/"forward" functionality for example? Also, how do I go to another page on a button click? Do I just use commands? If so, where does the EventAggerator used in most of the other examples come in handy?Marriage
Whether you use Conductor or one of the collection Conductors will depend on if you want your prev/next screens to be closed or just deactivated. To go to another page, you just call the ActivateItem method. Have a look at Actions on the documentation pages for invoking methods on your viewmodels from your views.Serpentine
Thanks. Could you please tell me how I can change the current active view to another within another viewmodel (changing to NewViewModel from withing OldVieModel for example.)Marriage
Well the view models are likely not to have any knowledge of each other as the coupling reduces reuse. So, you could either use standard .net events or the CM event aggregator to notify the shell to conduct the change of view models.Serpentine
Wouldn't creating a static app-wide instance of the ShellViewModel and using it to access the ActivateItem method be much more useful?Marriage
No that would be a poor design, follow the principle of least knowledgeSerpentine
C
4

In ShellView you use a content control like this:

<ShellView xmlns:cal="http://caliburnproject.org/">
     <StackPanel>
           <Button Content="Show other view" cal:Message.Attach="ShowOtherView" />
           <ContentControl cal:View.Model="{Binding Child}" />
     </StackPanel>
</ShellView>

ShellViewModel:

public class ShellViewModel : Screen
{
     private object Child;

     public object Child
     {
           get{ return child; }
           set
           {
                if(child == value)
                     return;
                child = value;
                NotifyOfPropertyChange(() => Child);
           }
     }

     public ShellViewModel()
     {
         this.Child = new MainViewModel();
     }

     public void ShowOtherView()
     {
           this.Child = new FooViewModel();
     }
}

So this is a very basic example. But as you see, your ShellView provides a ContentControl, which shows the child view. This ContentControl is bound via View.Model to the Child property from your ShellViewModel.

In ShellView, I used a button to show a different view, but you can also use a menu or something like that.

Cule answered 14/5, 2013 at 14:35 Comment(1)
You should for clarity declare your 'Child' instance to be of some base 'ViewModel' type, like 'PropertyChangedBase' or 'Screen' ...Metralgia

© 2022 - 2024 — McMap. All rights reserved.