** There is an excellent blog on Navigation with detailed information on each class and different styles @ devlicio.us for CM **
In the previous blog we look at how to do page navigation with Jounce. Today we will look at how to implement the same thing in Caliburn.Micro. The requirement is same.
I. Create a web page with two buttons and a container panel. The first buttons says ‘Hello World’ and second button says ‘Hello World1’.
II. Create two pages, with one page says ‘Hello world’ and the second page says ‘Hello World’.
III. Wire up the button events in such a way, when a user click the ‘Hello World’, it displays the page with ‘Hello World’ in the container panel. When the user presses ‘Hello World1’ button, it replaces the ‘Hello World’ page with ‘Hello World1’ page.
This is exactly the same requirement of our Jounce example. So how would we go about doing it? We start from what we learned from our ‘Hello World’ example.
I.Create web page with its contents:
Follow the steps specified in ‘Hello World’ example and create the simple Caliburn application. Make sure it compiles and runs. Lets add the button and content control to hold the panel to host the dynamic page by adding the following code to the ShellView.xaml
<Grid Background="White" d:DataContext="{d:DesignInstance sampleData:SampleShellViewModel, IsDesignTimeCreatable=True}"> <Grid.ColumnDefinitions> <ColumnDefinition Width="161*" /> <ColumnDefinition Width="194*" /> <ColumnDefinition Width="163*" /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="34*" /> <RowDefinition Height="253*" /> </Grid.RowDefinitions> <Button Content="Hello World" Name="HelloWorldButton" Grid.Row="0" Grid.Column="0"/> <Button Content="Hello World1" Name="HelloWorld1Button" Grid.Row="0" Grid.Column="2"/> <ContentControl Grid.Row="1" Grid.ColumnSpan="3" Name="ActiveItem"/> </Grid>
We have added two buttons, in row 0, column 0 and 2. First button shows ‘Hello World’ and the second one shows ‘Hello World1’ as the requirement. The third control we added is ‘ContentControl to show a single content. It is named as ‘ActiveItem’. This name is important and we will come to that soon. Other than that, there is nothing special in here, the button have Name associated with it, which is nothing but caliburn convention to bind to a method for click event in the view model. With XAML added your design time screen should look like the following
II. Create two pages to display ‘Hello World’ and ‘Hello Word1’:
Add two new Silverlight Pages (you can also use Silverlight User Controls if you want). Call it Page1View and Page2View. Remember, it is important to end all the view with ‘View’ and ViewModels with ‘ViewModels’. Caliburn uses convention to find and bind the View with ViewModels. Add a text block in each of the page to distinguish which page it is. So the page1view XAML would look like the following
<Grid x:Name="LayoutRoot"> <TextBlock Text="Hello World"/> </Grid>
While the Page2view.xaml would look like this
<Grid x:Name="LayoutRoot"> <TextBlock Text="Hello World1"/> </Grid>
Now that we have views, it is not over. Caliburn relies on ViewModel to do the routing, so create two classes with Page1ViewModel and Page2ViewModel. Both will be nothing but empty classes. You do not need to add any code. There is also different way to approach this same two screens, since I want to keep it simple, I am not going to discuss about them here yet.
III. Wireup the events to load pages:
Now that we have Shell to display the main page, two separate pages which display ‘Hello World’ and ‘Hello World1’. We need to wire up the button click events to activate appropriate page and display them in the content panel named ‘ActiveItem’. As you know by now, when you click a button, Caliburn will look for a method name with exact name of the Button. So Lets look at the button name of ‘Hello World’, it is called ‘HelloWorld’ (with out space between hello and world). We need to write a method in the shell view model to activate the page1 and display it. To do that. we write the HelloWorld method as follows in the ShellViewModel.cs.
[Export(typeof(IShell))] public class ShellViewModel : Conductor<object>, IShell { public void HelloWorldButton() { ActivateItem(new Page1ViewModel()); } public void HelloWorld1Button() { ActivateItem(new Page2ViewModel()); } }
That’s it. Now if you would run the application, you will get to the main page
now if you would press ‘Hello World’ button, it displays the Page1View in the content panel
If you would press ‘Hello World1’ it displays
So if you notice, we did not write lot of code to achieve the simple page navigation. In fact, the pages we created to display the ‘Hello World’ and ‘Hello World1’ do not have any clue about this will be placed on a particular panel or the page by itself did not say anything about it is being used to display somewhere. All the navigation work is done at the Shell level and nothing at the page level. Couple of important points here
1. If you notice, the ShellViewModel is derived from Conductor<object>, which is responsible for orchestrating the navigation.
2. To create and place an item on a panel, all you have to do is ActivateItem(viewmodel).
3. When ActivateItem is called, the screen instance is created and placed on ‘ActiveItem’ in the ShellView.
4. There is a whole a lot you can do during the navigation process and I barely touched the surface. We will look at more features in the navigation in later posts.