We continue our journey about Caliburn Micro and how to use this MVVM framework in a Windows Phone 8 application by understanding how to manage collections and navigation.

###

Collections

One of the most common scenarios in a Windows Phone application is the usage of collections: we have a list of data and we need to display it in a ListBox or in LongListSelector. The standard approach is to create a collection in the code (usually, when we’re talking about the XAML world, we use an ObservableCollection) and then to bind it to the ItemsSource property. Guess what? Caliburn Micro has another naming convention for that! And a pretty smart one too! To assign the collection to the ItemsSource property we use the standard naming convention we already know: the name of the property in the view model should match the name of the control. But here comes the smart decision: if you create a property which name is Selected followed by the name of the control in singular, it will be automatically bound with the SelectedItem property of the control. Let’s make an example:

<ListBox x:Name="Items">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel>
                <TextBlock Text="{Binding}" />
            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

 

In the XAML we add a ListBox with a simple template: it will simply display, for every item in the collection, a TextBlock with the value of the single item. The name of the ListBox is Items so, by following the Caliburn Micro naming convention, we expect to have in the view model a property called Items, which is the collection that will be displayed.

private ObservableCollection<string> items;

public ObservableCollection<string> Items
{
    get { return items; }
    set
    {
        items = value;
        NotifyOfPropertyChange(() => Items);
    }

}

And here’s come the magic:

private string selectedItem;

public string SelectedItem
{
    get { return selectedItem; }
    set
    {
        selectedItem = value;
        NotifyOfPropertyChange(() => SelectedItem);
        MessageBox.Show(value);
    }
}

As you can see the name of the property is SelectedItem, which is the name of the other property in singular (Items –> Item) prefixed by the word Selected. In the setter of the property we’ve added a line of code to display, using a MessageBox, the value of the selected property, that will be helpful for our tests. Now let’s try it: in the public constructor of the view model let’s add some fake data and assign it to the Items property.

public MainPageViewModel()
{
    Items = new BindableCollection<string>
                {
                    "Matteo",
                    "Mario",
                    "John"
                };
}

If you launch the application and you’ve correctly followed the steps the ListBox will contain the three test values we’ve defined: by tapping on one of the elements a MessageBox with the selected name will appear.

list1list2

###

Usually managing the navigation from a page to another of the application is one of the trickiest tasks using MVVM. The biggest problem to face is that in Windows Phone we use the NavigationService, that can be used only in the code behind that is connected to a view. You can’t directly access to it from another class, for example, like a ViewModel. To support the developer Caliburn Micro comes with a built in NavigationService, that can be used in a ViewModel by simply adding a reference in the public constructor of the application. The built in dependency injection container will take care of resolving the dependency for you and will give you access to it. So, the first thing to use manage navigation in a MVVM application developed with Caliburn Micro is to change the public constructor of your view model, like in the following example:

public class MainPageViewModel : PropertyChangedBase
{
    private readonly INavigationService navigationService;

    public MainPageViewModel(INavigationService navigationService)
    {
        this.navigationService = navigationService;
    }
}

From now on, you’ll be able to use the NavigationService inside your view model: you won’t have to register anything in the bootstrapper, since Caliburn Micro will take care of everything (as for every other service that is embedded with the toolkit).

The NavigationService that comes with the toolkit supports a view model first approach: instead of declaring which is the URL of the page where we want to take the user (that is the standard approach), we declare which is the ViewModel we want to display. The service will take care of creating the correct URL and display the view that is associated with the view model. Let’s see how does it work.

First we need a new page where to redirect the user: add to your project a new page and a new class in the ViewModels folder. Remember to use the naming convention we’ve learned in the second post of the series: if the name of the page is Page2View.xaml, the name of the ViewModel will have to be Page2ViewModel.cs. Before moving on, you have to remember to register the new view model in the Configure method of the bootstrapper, like in the following example:

protected override void Configure()
{
    container = new PhoneContainer(RootFrame);

    container.RegisterPhoneServices();
    container.PerRequest<MainPageViewModel>();
    container.PerRequest<Page2ViewModel>();
    AddCustomConventions();
}

Now add a button in the main page of your application and, using the naming convention we’ve learned in the previous post, assign to it a method in your view model, that will trigger the navigation towards the second page.

<Button Content="Go to page 2" x:Name="GoToPage2" />

 

public void GoToPage2()
{
    navigationService.UriFor<Page2ViewModel>()
        .Navigate();
}

With the **UriFor** method of the navigation service we get the needed URL for our view model, then we call the **Navigate**()method to trigger the navigation and redirect the user to the requested page.

You should already know that, when navigating from a page to another, you are able to carry some parameters in the query string, that can be used in the new page, like in the following sample

/Page2View.xaml?Name=Matteo

Using the NavigationContext you are able, in the view Page2View.xaml, to retrieve the value of the Name property that is passed using a query string. How to do it using Caliburn Micro? The first thing is to define, in our destination page’s view model (in our example, the class Page2ViewModel) a property, that will hold the  value of the parameter.

public class Page2ViewModel: PropertyChangedBase
{
private string name;

public string Name
{
    get { return name; }
    set
    {
        name = value;
        NotifyOfPropertyChange(() => Name);

    }
}

Then, we change the navigation operation like this:

public void GoToPage2()
{
    navigationService.UriFor<Page2ViewModel>()
                     .WithParam(x => x.Name, "Matteo")
                     .Navigate();
}

We’ve added the method WithParam, that accepts two parameters: the first one is the property of the destination view model that will hold our value and it’s specified using the lambda syntax (x represents the destination view model, in our example the instance of the Page2ViewModel class); the second parameter is the value that the property will have. When the Page2View.xaml view will be loaded, the Page2ViewModel will hold, in the Name property, the value of the parameter we’ve passed during the navigation, so we can use it for our purposes. For example, we can simply display it by adding a in the XAML a TextBlock with the same name of the property (do you remember the naming convention?)

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
    <StackPanel>
        <TextBlock x:Name="Name"></TextBlock>
    </StackPanel>
</Grid>

Important! It’s true that Caliburn Micro does a lot of magic, but the navigation with parameter feature is still based on query string parameter. The only magic is that these parameters are automatically injected in the properties of your view model, but they still are strings: you can use the WithParam method to pass to the new view just plain values, like strings and number. You can’t use it to pass complex objects.

To be continued

The journey is not ended yet, we still have to see how to manage messages and tombstoning with Caliburn Micro. Coming soon in the next posts! While you wait, you can download the sample project and start playing with it!

 

The Caliburn Micro posts series

  1. The theory