First steps with Caliburn Micro in Windows Phone 8 – Lazy loading with Pivot

After that, in the previous posts, we’ve talked about using the Panorama and Pivot controls in a Windows Phone application with Caliburn Micro, we’re ready to face the lazy loading topic. What is lazy loading? This approach is commonly used when working with databases and, simplifying, means that the data is loaded only when you really need it. Basically all the available ORM technologies (like Entity Framework or NHibernate) support lazy loading; think about a database, with many tables and relationships. Usually, when you do a query to retrieve the data that is the result of a join between two or more tables, the operation is immediately executed and all the requested data is retrieved from the database. With the lazy loading approach, instead, we are able to request for the data only when we need to work with it. Let’s say you have an application used to display orders and you want to do a query to get all the orders with the information about the customers that made them. With lazy loading, you are able to get all the orders and query for the related customer only when the user, actually, chooses to see his details: otherwise, the query is not executed.

How can this concept be applied to Pivot? With Caliburn Micro we’ve been able to split the different pages in the different Views and ViewModels but, in the end, they are all part of a single page: the conductor’s one, that contains the Pivot control. This means that, if we use the usual approach to load the data in the ViewModel’s constructor, when the user navigates to the page all the ViewModels are loaded and all the data is loaded and displayed.

Think about a news reader application, that displays different news categories in the different pages of a Pivot control: when the page is loaded, all the news are loaded, even the ones that are displayed in pages that are initially hidden. This is when lazy loading comes in handy, to improve the performances of our application: we can load just the data on the page that the user is currently viewing and loading the other stuff only when he swipes on that specific page.

Let’s see how to implement it: we’ll start from the same application we’ve developed in the last post, with a Pivot control and two pages.

IMPORTANT! Even if, from a code point of view, Panorama and Pivot have the same approach, we’ll be able to support lazy loading just using a Pivot, due to a bug in the Panorama control in Windows Phone 8. Which is the problem? That if Panorama items are added to the Panorama control using binding and the ItemsSource property (and they aren’t directly declared in the XAML or manually added in the code behind), the SelectedIndex property (which is important to keep track of the view that is actually visible) doesn’t work properly and returns only the values 0 and 1. The consequence is that the SelectedItem property doesn’t work properly, so we are not able to support lazy loading because we don’t know exactly when a view is displayed.

We’re going to develop a simple RSS reader, so first we’ll need some helpers class to accomplish our task.

The first one is the FeedItem class, which represents a single news from the RSS feed:

public class FeedItem
{
    public string Title { get; set; }
    public string Description { get; set; }
}

It’s a simple class that is used to store the title and the description of the news. Then we need a simple parser, to convert the XML that we get into a collection of FeedItem objects:

public static class RssParser
{
    public static IEnumerable<FeedItem> ParseXml(string content)
    {
        XDocument doc = XDocument.Parse(content);
        var result = doc.Descendants("rss").Descendants("channel").Elements("item").Select(x => new FeedItem
                                                                {
                                                                    Title = x.Element("title").Value,
                                                                    Description = x.Element("description").Value
                                                                });

        return result;
    }
}

By using LINQ to XML we are able to parse the RSS file and to extract, for every news (stored in the item node), the properties title and description.

Now we are ready to add the needed code to load the data: first let’s try to do it in the usual way, so we can see the difference. Our goal is to display a list news in the two Pivot pages, so we need, in the View, a ListBox to display them and, in the ViewModel, to retrieve the RSS, parse it and assign to a collection of FeedItem objects.  Here is the XAML:

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

Nothing special to highlight: the ItemTemplate is really simple, we simply show the title of every news. We give to the ListBox control the name FeedItems: if you remember the post about the collections naming convention, we expect to have, in the ViewModel, a collection called with the same name, that will host the data to display. And here is the ViewModel:

public class PivotItem1ViewModel: Screen
{
    public PivotItem1ViewModel()
    {
        DisplayName = "Pivot 1";
        LoadData();
    }

    public async void LoadData()
    {
        WebClient client = new WebClient();
        string result = await client.DownloadStringTaskAsync("http://feeds.feedburner.com/qmatteoq_eng");
        IEnumerable<FeedItem> feedItems = RssParser.ParseXml(result);

        FeedItems = feedItems.ToList();
    }

    private List<FeedItem> _feedItems;

    public List<FeedItem> FeedItems
    {
        get { return _feedItems; }
        set
        {
            _feedItems = value;
            NotifyOfPropertyChange(() => FeedItems);
        }
    } 
}

When the ViewModel is initialized we call the LoadData() method which, using the WebClient class and the Async Targeting Pack (so that we can use the DownloadStringTaskAsync() async method), we download the RSS feed. Then, we parse the result using the RssParser class we’ve created before and, in the end, we convert the collection we get in return in a List<T> object, that we assign to a property in the ViewModel called FeedItems. Due to the naming convention, this is the collection that will be displayed in the ListBox in the View.

Now repeat the same steps for the second page of the Pivot, by adding the same code to the PivotItem2View page and to the PivotItem2ViewModel class: just change the URL of the RSS feed to download with another one, so that we can see the differences.

Now run the application: you’ll see that, when the Pivot page is loaded, both views will load and display the list of news from the RSS item; not just the one that is currently displayed, but also the second one.

If you have many views in the Pivot and you need to load a lot of data, this operation can take some time and the user will have to wait that everything is fully loaded before using the application. So, let’s implement lazy loading! The operation is really simple and, if you have already read my previous posts about Caliburn Micro, you should already have a hint about the way to do it: using the navigation events.

In this post we’ve talked about the Screen class and how, by inheriting our ViewModels from it, we are able to hook to many navigation events: specifically, the OnActivate() method is raised when the view is displayed. In a Panorama’s or Pivot’s context, this event is raised when the user swipes to that specific view: this means that we are able to use it to load the data only when the user navigates to that specific page. It’s enough to move the code inside the LoadData() method in the OnActivate() one to accomplish our goal:

public class PivotItem1ViewModel: Screen
{
    public PivotItem1ViewModel()
    {
        DisplayName = "Pivot 1";
    }

    protected override async void OnActivate()
    {
        base.OnActivate();
        WebClient client = new WebClient();
        string result = await client.DownloadStringTaskAsync("http://feeds.feedburner.com/qmatteoq_eng");
        IEnumerable<FeedItem> feedItems = RssParser.ParseXml(result);

        FeedItems = feedItems.ToList();
    }

    private List<FeedItem> _feedItems;

    public List<FeedItem> FeedItems
    {
        get { return _feedItems; }
        set
        {
            _feedItems = value;
            NotifyOfPropertyChange(() => FeedItems);
        }
    } 
}

If you launch again the application your notice that, now, when the Pivot is loaded only the news in the first page will be displayed: the second page won’t contain any data. As soon as we swipe to the second page, the load operation is triggered and the list of news is displayed in the ListBox.

That’s all

This is the conclusion of the series of posts about using the Pivot control in a Windows Phone application developed using the MVVM pattern with the help of Caliburn Micro.

Posted in Windows Phone | Tagged , , | Leave a comment

First steps with Caliburn Micro in Windows Phone 8 – Pivot

Panorama and pivot are two key concepts in Windows Phone development: they are, probably, the most used UI paradigm in applications, since they allow to create interfaces that are very different (and, for this reason, original) from an iOS or Android applications.

Panoramas are used, usually, as an entry point for the application: it features many pages, that can be browsed by the user simply by swiping on the left or on the right of the screen; every page shows a little preview of the content of the next page, so the user can understand that there’s more to see. They are a starting point for the app, not a data container: a panorama should be used to give a quick peek of what the application offers and a way to access to all the different sections and options. For example, if you’re developing a news reader, it’s not correct to display all the news in a panorama page; it’s correct, instead, to display just the latest 10 news and add a button to go to another section of the application to see all the news.

Pivots, instead, are used to display different information related to the same context, or the same information but related to different items. As example of the first scenario, take the contact’s detail page in the People hub: in this case, the Pivot control is used to display different information (details, pictures, social network’s updates, etc.) related to the same context (the person). A weather app, instead, is a good example of the second scenario: a Pivot item can be used to display the same information (the weather forecast) but for different items (the cities).

Managing a Panorama or a Pivot in a MVVM application isn’t trivial: since the pages that are part of a Panorama or a Pivot are “fake” (they aren’t real different XAML pages, but different item controls, PivotItem or PanoramaItem, inside the main one, Panorama or Pivot, all placed in the same page), it’s enough to have a View and a ViewModel and connect them (and the data exposed by the ViewModel) using the Caliburn Micro conventions we’ve learned to use in the previous post.

But there’s a smarter approach to that, which can be used to support features like lazy loading and to have a better organized code. Let’s see what it’s all about.

IMPORTANT! Even if, from a code point of view, Panorama and Pivot have the same approach, we’ll be able to implement the mechanism I’m going to talk about just using a Pivot, due to a bug in the Panorama control in Windows Phone 8. Which is the problem? That if Panorama items are added to the Panorama control using binding and the ItemsSource property (and they aren’t directly declared in the XAML or manually added in the code behind), the SelectedIndex property (which is important to keep track of the view that is actually visible) doesn’t work properly and returns only the values 0 and 1. The consequence is that the SelectedItem property doesn’t work properly, so we are not able to know exactly when a view is displayed.

The Conductor class

Caliburn Micro supports the concept of Conductor: a series of pages that are connected together and that are managed by a single entry point. With this approach, we’re able to have a main page, which acts as conductor, and different pages with different view models, that are the items of the Pivot or the Panorama. The first advantage of this approach should be already clear: instead of having a unique ViewModel and a unique View, that should manage all the items inside the control, we have separate Views and separate ViewModels: this will help us a lot to keep the project cleaner and easier to maintain.

Let’s start to do some experiments using a Pivot. First we need to create the Conductor page, that will contain the Pivot control: let’s add a page in the Views folder of the project (for example, PivotView) and a ViewModel in the ViewModels folder (using the standard naming convention, it should be PivotViewModel). Don’t forget to register it in the Configure() method of the bootstrapper!

Now let’s start with the ViewModel definition:

public class PivotViewModel: Conductor<IScreen>.Collection.OneActive
{
    private readonly PivotItem1ViewModel item1;
    private readonly PivotItem2ViewModel item2;

    public PivotViewModel(PivotItem1ViewModel item1, PivotItem2ViewModel item2)
    {
        this.item1 = item1;
        this.item2 = item2;
    }

    protected override void OnInitialize()
    {
        base.OnInitialize();

        Items.Add(item1);
        Items.Add(item2);

        ActivateItem(item1);
    }
}

First, the ViewModel needs to inherit from the class Conductor<T>.Collection.OneActive: we’re telling to the ViewModel that it will hold a collection of views (since T is IScreen, which is the base interface for the Screen class) and, with the OneActive syntax, we’re telling that we are in scenario where only one view can be active at the same time. This is the only option that can be used in Windows Phone to manage Panorama and Pivot controls: Caliburn Micro offers other options because it supports also other technologies like WPF or Silverlight, where multiple views can be active at the same tine.

Notice that, in the constructor, we have added two parameters, which types are PivotItem1ViewModel and PivotItem2ViewModel: these are the ViewModels that are connected to the pages that we’re going to display in the Pivot control and that we’re going to create later.

Then we override the OnInitialize() method, that is called when the page is initialized for the first time: since we’re inheriting from the Conductor<T> class, we have access to two important helpers: the Items property and the ActivateItem() method. The first one is a collection of elements which type is T (the one that has been passed to the Conductor<T> definition): it will contains all the pages of our Pivot control, so we simply add all the ViewModels we’ve added in the constructor. Then we call the ActivateItem() method, that focus the view on the specified page: in this case we’re setting the focus on the first one, but we could have done the same on another page. For example, we could have received the page to display as a query string parameter in the navigation url, from another page or a secondary tile.

Now we need to create the other pages, that will compose our Pivot: simply add two new Views in the Views folder (called PivotItem1View and PivotItem2View) and the related ViewModels (called PivotItem1ViewModel and PivotItem2ViewModel). They are simple pages, nothing special to say about it: if you use the standard Windows Phone page template to create the View, just remember to remove the not needed stuff (since the page will be contained by the Pivot, the header with the application name and the page title are not needed). Here is a sample of a page:

<phone:PhoneApplicationPage
    x:Class="CaliburnMicro.Views.PivotItem1View"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    SupportedOrientations="Portrait" Orientation="Portrait"
    mc:Ignorable="d"
    shell:SystemTray.IsVisible="True">

    <!--LayoutRoot is the root grid where all page content is placed-->
    <Grid x:Name="LayoutRoot" Background="Transparent">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

      <!--ContentPanel - place additional content here-->
        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">

        </Grid>
    </Grid>

</phone:PhoneApplicationPage>

And here is the related ViewModel:

public class PivotItem1ViewModel: Screen
{
    public PivotItem1ViewModel()
    {
        DisplayName = "First pivot";
    }
}

Nothing special: it just inherits from the Screen class (like every other ViewModel we’ve created in the previous posts). Just notice that, in the constructor, I set a property called DisplayName, that is part of the Screen base class: it’s the name of the pivot item and it will be used as header.

Now it’s the turn to see the XAML of the Conductor page (the one called PivotView):

<Grid x:Name="LayoutRoot" Background="Transparent">
    <!--Pivot Control-->
    <phone:Pivot Title="MY APPLICATION" x:Name="Items" SelectedItem="{Binding ActiveItem, Mode=TwoWay}">
        <phone:Pivot.HeaderTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding DisplayName}" />
            </DataTemplate>
        </phone:Pivot.HeaderTemplate>
    </phone:Pivot>
</Grid>

You can notice some Caliburn magic here: the first thing is that the name of the Pivot control is Items; this way, it’s automatically connected to the Items property in the ViewModel, which contains the collections of view to add. The second thing is that the SelectedItem property is connected (in two way mode) to a property called ActiveItem. This property is declared in the Conductor<T> base class and it’s used to hold a reference to the ViewModel of the current screen. It’s important to set it, otherwise the ActivateItem() method we’ve seen in the PivotViewModel class won’t work.

The last thing to notice is that we override the HeaderTemplate of the Pivot control: we simply set a TextBlock in binding with the DisplayName property. This way, the title of the page will be automatically taken from the DisplayName property we’ve set in the page’s ViewModel.

Ok, now are we ready to test the application? Not yet! First we have to register, in the Configure() method of the boostrapper class, all the ViewModels we’ve just created (the conductor’s one plus all the single pages), otherwise Caliburn won’t be able to resolve them.

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

    container.RegisterPhoneServices();

    container.PerRequest<PivotViewModel>();
    container.PerRequest<PivotItem1ViewModel>();
    container.PerRequest<PivotItem2ViewModel>();

    AddCustomConventions();
}

Now you’re ready to test the application: if you did everything correct, you’ll see your Pivot with 2 pages, one for every View and ViewModel you’ve added. You can now play with the app: you can add some data to display in a specific page, or you can add more items to the Pivot control. It’s up to you!

In the next post we’ll see how to deal with the Pivot control and how to implement lazy loading. In the meantime, have fun with the sample project!

The Caliburn Micro posts series

  1. The theory
  2. The first project
  3. Actions
  4. Collections and navigation
  5. Tombstoning
  6. Advanced navigation and deep links
  7. Messaging
  8. Using launchers and choosers
  9. Use your own services
  10. The Application Bar
  11. Pivot
  12. Lazy loading with pivot
Posted in Windows Phone | Tagged , , | 10 Comments

First steps with Caliburn Micro in Windows Phone 8 – The Application Bar

The Application Bar is the joy and the pain of every Windows Phone developer that starts to use the Model-View-ViewModel pattern to develop his applications. Sooner or later you’ll have to face this problem: the Application Bar is a special control, that doesn’t belong to the page and that doesn’t inherit from the FrameworkElement class. For this reason, binding simply doesn’t work: you can’t use commands to hook to the Tap event and you can’t bind a property of your ViewModel to the Text or IconUri properties.

Since in most of the cases dealing with the Application Bar will simply force you to “break” the MVVM pattern, many talented developers came up with a solution: an Application Bar replacement. There are many implementations out there: two of the bests I’ve found are the Cimbalino Toolkit by Pedro Lamas and Caliburn Bindable App Bar by Kamran Ayub. The first toolkit uses an approach based on behaviors, that are applied on the native application bar. We won’t discuss about this toolkit in this post because, even it’s great, it doesn’t play well with Caliburn: it’s been designed with support for MVVM Light in mind. We’ll focus on the Caliburn Bindable App Bar, which is a totally new control that replaces the standard Application Bar and that supports all the standard Caliburn naming conventions.

Let’s start!

Add the application bar to a project

The Caliburn Bindable App Bar is available as a NuGet package: simply right click on your project, choose Manage NuGet packages and look for the package called Caliburn.Micro.BindableAppBar. Once you’ve installed it, you’ll have to add the following namespace in the XAML to get a reference to the control:

xmlns:bab=”clr-namespace:Caliburn.Micro.BindableAppBar;assembly=Caliburn.Micro.BindableAppBar”

Once you’ve done it, you can add the real control which is call BindableAppBar. But, pay attention! Unlike the real ApplicationBar control (that is placed outside the main Grid,  because is not part of the page), this control should be placed inside the Grid, right before the closing tag (I’m talking about the Grid that, in the standard template, is called LayoutRoot). Here is a sample:

<Grid x:Name="LayoutRoot" Background="Transparent">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>

    <!--TitlePanel contains the name of the application and page title-->
    <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
        <TextBlock Text="Caliburn Micro" Style="{StaticResource PhoneTextNormalStyle}" Margin="12,0"/>
        <TextBlock Text="Sample" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
    </StackPanel>

    <!--ContentPanel - place additional content here-->
    <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">

    </Grid>

    <bab:BindableAppBar x:Name="AppBar">
        <bab:BindableAppBarButton x:Name="AddItem"
                                  Text="{Binding AddItemText}"
                                  IconUri="{Binding Icon}" 
                                  Visibility="{Binding IsVisible, Converter={StaticResource BooleanToVisibilityConverter}}"
                                  />

        <bab:BindableAppBarMenuItem x:Name="RemoveItem"
                                  Text="Remove"
                                  />
    </bab:BindableAppBar>
</Grid>

The control is very simple to use! Inside the BindableAppBar node you can add two items: BindableAppBarButton, which is the icon button (remember that you can add up to four icons) and BindableAppBarMenuItem, which is one of the text items that are displayed under the icons.

They share the same properties, because they both are buttons that can be tapped to trigger an action: the only difference is that the BindableAppBarButton control has an IconUri property, which contains the path of the image that is displayed as icon.

Both controls share the same Caliburn naming convention that is used for actions: the value of the x:Name property of the control is the name of the method, declared in the ViewModel of the page, that is triggered when the user taps on it. The best part of this control that all the other properties supports binding, even the Visibility property, that can be used to show or hide an item according to some conditions.

Before using it, there’s an important step to do: add a custom convention. Caliburn Micro supports a way to define your own custom conventions, that are added at the top of the already existing one. The place where to do this is in the boostrapper, inside the AddCustomConventions() method that, by default, is called when the boostrapper is registered.

Here is the code to insert:

static void AddCustomConventions()
{
    ConventionManager.AddElementConvention<BindableAppBarButton>(
    Control.IsEnabledProperty, "DataContext", "Click");
    ConventionManager.AddElementConvention<BindableAppBarMenuItem>(
    Control.IsEnabledProperty, "DataContext", "Click");
}

With this code basically we’re adding a convention to manage the Click event on the button, so that it’s enough to give to a method the same name of the Button control to bind them together.

Now it’s the ViewModel’s turn to manage the BindableAppBar:

public class MainPageViewModel: Screen
{

    private string addItemText;

    public string AddItemText
    {
        get { return addItemText; }
        set
        {
            addItemText = value;
            NotifyOfPropertyChange(() => AddItemText);
        }
    }

    private Uri icon;

    public Uri Icon
    {
        get { return icon; }
        set
        {
            icon = value;
            NotifyOfPropertyChange(() => Icon);
        }
    }

    private bool isVisible;

    public bool IsVisible
    {
        get { return isVisible; }
        set
        {
            isVisible = value;
            NotifyOfPropertyChange(() => IsVisible);
        }
    }

    public MainPageViewModel()
    {
        AddItemText = "Add";
        Icon = new Uri("/Assets/AppBar/appbar.add.rest.png", UriKind.Relative);
        IsVisible = false;  
    }

    public void AddItem()
    {
        MessageBox.Show("Item added");
    }

    public void RemoveItem()
    {
        MessageBox.Show("Item removed");
    }
}

Nothing special to say if you’ve already read the other posts about Caliburn Micro: we have defined some properties and methods, that are connected to the Application Bar using the Caliburn naming conventions. When the ViewModel is created, we set the text, the icon and the visibility status of one of the buttons in the Application Bar, instead of defining them in the XAML. This approach is very useful when the state of the buttons in the Application Bar needs to change while the application is executed. For example, think about an application to read news: the user is able to save a news in the favorites’ list using a button in the Application Bar. In this case, the status of the button should change according to the status of the news: if the news has already been marked as read, probably the text of the button will be something like “Remove” and the icon will display a minus sign; vice versa, if the news hasn’t been added yet to the list the button’s text will say “Add” and the icon will display a plus sign.

With the ViewModel we’ve defined, it’s simple to change some properties according to the status of the news and automatically reflect the change to the control in the XAML.

Also the Visibility property can come in handy in many situations: for example, let’s say that the same news application as before allows the user to pin a news in the start screen, by creating a secondary tile. In this case, only if the application is opened from a secondary tile we want to display a “Home” button in the Application Bar, to redirect him to the home page of the app; otherwise, we don’t need it, because the user can use the Back button to accomplish the same task. In this scenario, the Visibility property is perfect for us: it’s enough to change it according to the fact that the app has been opened from a secondary tile or not.

 

The Caliburn Micro posts series

  1. The theory
  2. The first project
  3. Actions
  4. Collections and navigation
  5. Tombstoning
  6. Advanced navigation and deep links
  7. Messaging
  8. Using launchers and choosers
  9. Use your own services
  10. The Application Bar
  11. Pivot
  12. Lazy loading with pivot
Posted in Windows Phone | Tagged , , | 14 Comments

First steps with Caliburn Micro in Windows Phone 8 – Use your own services

In this series of posts about Caliburn Micro we’ve learned that the toolkit includes many built in helpers and services, that are automatically registered when, in the bootstrapper, you call the RegisterPhoneServices() method of the PhoneContainer class. We’ve seen many examples of these services, like NavigationService and EventAggregator. We’ve been able to get a reference to these services by simply adding a parameter in the view model’s constructor.

But what to do if we need to register your own classes and services? Let’s say that you’re developing a RSS reader application, that is able to parse the XML of a RSS feed and display it in a Windows Phone application. In this case, you may have a service (that is simply a dedicated class) that takes care of downloading the RSS and to convert it from a plain string to real objects. One approach would be to create an instance of the service directly in the ViewModel and use it, for example:

public MainPageViewModel() 
{
    IFeedService feedService = new FeedService();
     // do something with the feed service
}

But this approach comes with a downside. Let’s say that you need to do some testing with fake data and, as a consequence, you want to switch the FeedService class with a fake feed service, that takes the data from a local XML file instead of using the real RSS feed. In a real scenario, probably your FeedService is used across multiple view models: in this case, you will have to go in every ViewModel and change the class that is used, like this:

public MainPageViewModel() 
{
    IFeedService feedService = new FakeFeedService();
     // do something with the fake feed service
}

A good approach would be to use the same one that has been adopted by Caliburn Micro: services are registered at startup using the built in dependency container so that it’s enough to add a parameter in the ViewModel to have the service automatically resolved at runtime and injected into the ViewModel. This way, if we need to change the implementation of our service, we will do it just in the bootstrapper, when the services are registered: automatically every ViewModel will start to use it.

Here is how the ViewModel definition will change:

public MainPageViewModel(IFeedService feedService)
{
    //do something with the feed service
}

Register your services

Let’s see how to implement your own services and register them, so that you’ll able to use the approach I’ve explained. First you need to create an interface for your service, that describes the methods that the class will expose. This is an important step because it will allow you to easily switch the implementation of the class with another one just by changing the way it’s registered in the bootstrapper. Let’s take the previous example about the fake feed reader class: both the real and fake feed service will inherit from the IFeedService interface and, in the ViewModel constructor, we will ask for a reference to that interface. This way, when we change the implementation in the boostrapper, everything will continue to work fine.

Let’s see a sample of the interface for our FeedService class:

public interface IFeedService
{
    Task<List<FeedItem>> GetNews(string url);
}

The interface describes just one method, that we will use to get the news list from the RSS feed: as parameter, it takes the URL of the RSS feed and returns a collection of FeedItem object, that is a simple class that describes some basic properties of a news item:

public class FeedItem
{
    public string Title { get; set; }
    public string Description { get; set; }
    public Uri Url { get; set; }
}

And here is the real implementation of the FeedService class:

public class FeedService: IFeedService
{
    public async Task<List<FeedItem>> GetNews(string url)
    {
        WebClient client = new WebClient();
        string content = await client.DownloadStringTaskAsync(url);

        XDocument doc = XDocument.Parse(content);
        var result = doc.Descendants("rss").Descendants("channel").Elements("item").Select(x => new FeedItem
        {
            Title = x.Element("title").Value,
            Description = x.Element("description").Value
        }).ToList();

        return result;
    }
}

In the class we actually write the real implementation of the GetNews method: it uses the WebClient class to download the RSS file (we use the DownloadStringTaskAsync() method that has been added by installing the Async pack using NuGet) and then, thanks to LINQ to XML, we extract the information we’re looking for (the title and the description of the news) and we store them in a new FeedItem object. At the end of the process, we have a collection of FeedItem objects:  each one of them contains a news that was stored in the RSS file.

Now it’s time to register our service, so that it can be used by our ViewModel. The registration is made in the bootstrapper and  you should be already familiar with it, since we’ve learned to register our view models every time we have added a new page to our project. We do it in the Configure() method of the bootstrapper class:

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

    container.RegisterPhoneServices();
    container.PerRequest<MainPageViewModel>();
    container.PerRequest<IFeedService, FeedService>();
    AddCustomConventions();
}

We can see a difference: since our FeedService has an interface, we need to register it in a different way than we did for the MainPageViewModel. In fact, we have to tell to the container that, every time a ViewModel requires an IFeedService object, we want to provide a FeedService implementation. For this reason, the container exposes a PerRequest<T, Y> overload, where T is the base interface and Y is the real implementation of the interface.

Now we are able to just use it in the view model of our page, to display the list of news. Here is a sample XAML of the page:

<StackPanel>
    <Button Content="Load website" x:Name="LoadWebsite"></Button>
    <ListBox x:Name="News">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <StackPanel>
                    <TextBlock Text="{Binding Path=Title}"></TextBlock>
                    <TextBlock Text="{Binding Path=Description}"></TextBlock>
                </StackPanel>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</StackPanel>

We have a button, that will execute the LoadWebsite method of the ViewModel (that will use our service to load the data), and we have a ListBox, which ItemTemplate simply displays the tile and the description of the news, one below the other.

And here is the ViewModel:

public class MainPageViewModel: Screen
{
    private readonly IFeedService feedService;

    private List<FeedItem> news;

    public List<FeedItem> News
    {
        get { return news; }
        set
        {
            news = value;
            NotifyOfPropertyChange(() => News);
        }
    }

    public MainPageViewModel(IFeedService feedService)
    {
        this.feedService = feedService;
    }

    public async void LoadWebsite()
    {
        News = await feedService.GetNews("http://feeds.feedburner.com/qmatteoq_eng");
    }
}

Nothing special here, except that we’ve added in the constructor a parameter which type is IFeedService: since we’ve registered it in the boostrapper, the parameter will contain a FeedService object, that we can use in the LoadWebsite() method to get the list of news using the GetNews() method we’ve defined in the service. This sample, we are parsing the RSS feed of this blog.

Use your own service to pass data between pages

When we talked about navigation we learned that Caliburn exposes a way to pass data between pages, that relies on the query string parameter that are supported by the OS. The problem of this approach is that, since we’re talking about parameters that are added to the navigation url, we can only send text parameters, like strings and numbers. In most of the cases, instead, we need to pass complex object. Take as example the RSS reader app we’ve just built: we want to implement a detail page, that displays all the information about the news selected by the user. In this case, when we navigate to the detail page, we want to carry the whole FeedItem object that has been selected.

One approach is to use your own service to store the data and pass it to every ViewModel that needs the information, exactly like we did for the FeedService.  Here is a sample of a DataService class:

public class DataService
{
    public FeedItem SelectedItem { get; set; }
}

As you can see it’s really simple, since it will be used just to store the FeedItem object that has been selected by the user in the ListBox.

Now we need to register it in the boostrapper:

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

    container.RegisterPhoneServices();
    container.PerRequest<MainPageViewModel>();
    container.PerRequest<DetailPageViewModel>();
    container.PerRequest<IFeedService, FeedService>();
    container.Singleton<DataService>();
    AddCustomConventions();
}

And here comes something new: we’re not registering it using the familiar PerRequest<T> method, but with the Singleton<T> method exposed by the PhoneContainer class. Which is the difference? When a class is registered using the PerRequest<T> method every time a ViewModel asks for a reference, it gets a new instance of the object. It’s the best approach when we’re dealing with view models: think about the DetailPageViewModel we’ve registered, that is connected to the page that displays the details of the selected news. In this case, every detail page will be different, because we’ll have to display a different news: by creating a new instance of the view model every time the user navigates to the detail page we make sure that the fresh data is correctly loaded.

This is not the case for our DataService: in this case we need to maintain a single instance across the application, because we want to take back and forth the FeedItem object selected by the user. If we would have registered it using the PerRequest<T> method, we would have lost the value stored in the SelectedItem property as soon as the user navigates away from the main page. The answer is using the Singleton<T> method: this way we’ll always get the same object in return when a ViewModel asks for it.

Now we just need to add a parameter which type is DataService in the constructor of both our view models: the main page one and the detail page one.

public class MainPageViewModel: Screen
{
    private readonly IFeedService feedService;
    private readonly INavigationService navigationService;
    private readonly DataService dataService;

    private List<FeedItem> news;

    public List<FeedItem> News
    {
        get { return news; }
        set
        {
            news = value;
            NotifyOfPropertyChange(() => News);
        }
    }

    private FeedItem selectedNew;

    public FeedItem SelectedNew
    {
        get { return selectedNew; }
        set
        {
            selectedNew = value;
            dataService.SelectedItem = value;
            navigationService.UriFor<DetailPageViewModel>().Navigate();
            NotifyOfPropertyChange(() => SelectedNew);
        }
    }

    public MainPageViewModel(IFeedService feedService, INavigationService navigationService, DataService dataService)
    {
        this.feedService = feedService;
        this.navigationService = navigationService;
        this.dataService = dataService;
    }

    public async void LoadWebsite()
    {
        News = await feedService.GetNews("http://feeds.feedburner.com/qmatteoq_eng");
    }
}

We’ve added a property called SelectedNew: if you remember what I’ve explained in this post, you’ll know that by using this naming convention we are able to get automatically, in the SelectedNew property, the item selected by the user in the ListBox that is connected to the News collection.

Inside the setter of this property we do two additional things: the first one is to store the selected value in the SelectedItem property of the DataService class. The second is to redirect the user to the detail page, using the built in NavigationService.

What about the second page? The view it’s very simple, since it’s used just to display the Title and Description properties of the selected item.

<StackPanel>
    <TextBlock x:Name="Title" />
    <TextBlock x:Name="Description" />
</StackPanel>

And the ViewModel is really simple too:

public class DetailPageViewModel: Screen
{
    private string title;

    public string Title
    {
        get { return title; }
        set
        {
            title = value;
            NotifyOfPropertyChange(() => Title);
        }
    }

    private string description;

    public string Description
    {
        get { return description; }
        set
        {
            description = value;
            NotifyOfPropertyChange(() => Description);
        }
    }

    public DetailPageViewModel(DataService dataService)
    {
        Title = dataService.SelectedItem.Title;
        Description = dataService.SelectedItem.Description;
    }
}

We add a parameter in the constructor which type is DataService: this way the bootstrapper will give us the correct instance that, since has been registered as singleton, will be the same that was used by the MainPageViewModel. This way, the SelectedItem property of the DataService will contain the item selected by the user in the main page: we simply use it to set the Title and Description properties, so that they are displayed in the page.

That’s all for this topic! But don’t be afraid, there are some other subjects to talk about in the next posts Smile

 

The Caliburn Micro posts series

  1. The theory
  2. The first project
  3. Actions
  4. Collections and navigation
  5. Tombstoning
  6. Advanced navigation and deep links
  7. Messaging
  8. Using launchers and choosers
  9. Use your own services
  10. The Application Bar
  11. Pivot
  12. Lazy loading with pivot
Posted in Windows Phone | Tagged , , | 8 Comments

First steps with Caliburn Micro in Windows Phone 8 – Using launchers and choosers

In the previous post we’ve seen how the messaging infrastructure works in Caliburn Micro. In this post we’ll see how to use launchers and choosers within a ViewModel: the two topics are strictly connected, since to accomplish this task Caliburn uses a special type of messages. In fact, when we want to interact with launchers and choosers, we exchange messages using the IEventAggregator class we learned to use in the previous post. The difference is that messages are “hidden” and already built in in the toolkit, so we won’t need to define them. Let’s start!

I assume that, since you’re reading this blog and since MVVM is an “advanced “ topic, you already know what launchers and choosers are. In case not, it’s a way that the operating system offers to interact with native applications: for example, displaying a position in the native Map app; or import a contact from the People hub; or send a SMS using the Messages app.

Launchers

Let’s see how to interact with launchers, that are the tasks that are used to demand an operation to a native application, without expecting anything in return: we’ll do this by using the MapsTask class, that is used to display a location in the native maps application. I assume that you already have an application that is created using the Caliburn toolkit, so that you have a View connected to a ViewModel.

In the View we add a button, that we will use to execute the launcher:

<StackPanel>
    <Button Content="Launch map" x:Name="LaunchMap" />
</StackPanel>

As usual, thanks to the Caliburn naming conventions, the click on the button will trigger the LaunchMap method in the ViewModel. Let’s take a look at the ViewModel:

public class MainPageViewModel: Screen
{
    public MainPageViewModel(IEventAggregator eventAggregator)
    {
        this.eventAggregator = eventAggregator;
        eventAggregator.Subscribe(this);
    }

    public void LaunchMap()
    {
        eventAggregator.RequestTask<MapsTask>(task =>
                                                  {
                                                      task.SearchTerm = "Milan";
                                                  });
    }
}

As we’ve seen in the previous post, it’s enough to add in the constructor a parameter which type is IEventAggregator to have a reference to the real object in our ViewModel. The first step is to subscribe to receive messages, by using the Subscribe method, passing the value this as parameter (since we want to register the ViewModel itself).

In the LaunchMap method we use again the EventAggregator, by calling an extension method that is available exclusively for Windows Phone: RequestTask<T>, where T is the launcher we want to execute (selected by one of the objects that is available in the Microsoft.Phone.Tasks namespace). As parameter of the method we can pass a delegate (in this sample, we use a lambda expression): this is needed for all the launchers or choosers that need some properties to be set to work properly. In this sample, we set the SearchTerm property of the task, to specify the location we want to search on the map. If we’re going to use a launcher that doesn’t need any parameter (like the MarketplaceDetailTask), we can simply pass nothing to the method.

If you try this code on the emulator or on the phone, you’ll see the Maps app opening and locating the Milan position as soon as you’ll tap on the button.

Choosers

Choosers work in a little bit different mode: the way we’re going to execute it is the same that we’ve seen for launchers but, in this case, we expect also a result in return, that we need to manage. As I’ve already told you, launchers and choosers infrastructure in Caliburn is based on messaging, so the way we’re going to manage results is the same we’ve seen in the previous post to manage incoming messages. Let’s see how it works using the PhoneNumberChooserTask, that can be used to import a contact from the People hub to the application.

First, let’s add a new button in the XAML:

<StackPanel>
    <Button Content="Open contact" x:Name="OpenContact" />
</StackPanel>

Now let’s take a look at the ViewModel:

public class MainPageViewModel: Screen, IHandle<TaskCompleted<PhoneNumberResult>>
{
    private readonly IEventAggregator eventAggregator;

    public MainPageViewModel(IEventAggregator eventAggregator, INavigationService navigationService)
    {
        this.eventAggregator = eventAggregator;

        eventAggregator.Subscribe(this);
    }

    public void OpenContact()
    {
        eventAggregator.RequestTask<PhoneNumberChooserTask>();
    }

    public void Handle(TaskCompleted<PhoneNumberResult> message)
    {
        MessageBox.Show(message.Result.DisplayName);
    }
}

The way a chooser works is the same as launchers: we call the RequestTask<T> method of the EventAggregator, passing as value of the T parameter the chooser’s type (PhoneNumberChooserTask). In this case, we don’t pass any parameter, since the chooser doesn’t require any property to be set to work properly. To manage, instead, the return value we use the same approach we’ve seen to receive a message: we inherit our ViewModel from the classe IHandle<T>, where T is TaskCompleted<T>. In this case, T is the class used by the operatying system to return a chooser’s result to the caller: in this sample, it’s a PhoneNumberResult, since it contains the phone number of the selected contact.

Once we inherit the ViewModel from this class, we need to implement the Handle method, that receives as a parameter the result returned by the chooser. It’s up to us what to do: in this sample, we simply take the DisplayName property (which contains the name of the selected contact) and we show it using a MessageBox.

How to correctly manage the launchers and choosers lifecycle

There’s a problem in using the messaging architecture to manage launchers and choosers. When you subscribe a ViewModel to wait for incoming messages using the Subscribe method of the EventAggregator, the subscription is kept alive until the ViewModel instance itself is alive. In case of launchers and choosers this can cause some issues: if you rememer in the previous post, we were able to exchange messages between two different ViewModels (more precisely, it’s the reason why messages are used); in this case, instead, we’re using the messaging infrastructure to exchange messages inside a single ViewModel. What happens in the end is that if, for example, you move the OpenContact method to another ViewModel, but you keep the MainPageViewModel registered to listen for incoming TaskCompleted<PhoneNumberResult> message, you will notice that the MainPageViewModel will answers to requests that are triggered by other view models. This can cause some problems, especially if you need to use the same launcher or chooser in multiple views, so you have many view models registered to receive the same message.

This is valid especially for the MainViewModel: since it’s connected to the first page of the application, its instance is always alive until the app is closed, unlike for the other pages of the application (if you’re in the second page of your application and you go back to the main page, the second page is dismissed and its ViewModel is disposed).

The best way to manage this situation is to use the navigation events we’ve learned to use in a previous post and to inherit our ViewModel from the Screen class: this way we can call the Subscribe method of the EventAggregator class when the user navigate towards the page (managed by the OnActivate method) and call the Unsubscribe method, instead, when the user leaves the page (managed by the OnDeactivate method). With this approach, when the user navigates to another page the previous page isn’t subscribed anymore to receive messages and, as a consequence, will ignore any interaction with launchers and choosers.

Here is a sample of the updated ViewModel

public class MainPageViewModel: Screen, IHandle<TaskCompleted<PhoneNumberResult>>
{
    private readonly IEventAggregator eventAggregator;

    public MainPageViewModel(IEventAggregator eventAggregator)
    {
        this.eventAggregator = eventAggregator;
    }
    protected override void OnActivate()
    {
        eventAggregator.Subscribe(this);
        base.OnActivate();
    }

    protected override void OnDeactivate(bool close)
    {
        eventAggregator.Unsubscribe(this);
        base.OnDeactivate(close);
    }

    public void OpenContact()
    {
        eventAggregator.RequestTask<PhoneNumberChooserTask>();
    }

    public void Handle(TaskCompleted<PhoneNumberResult> message)
    {
        MessageBox.Show(message.Result.DisplayName);
    }
}

As you can see, we don’t call anymore the Subscribe method of the EventAggregator class in the ViewModel constructor, but we’ve moved it in the OnActivate method.

Using the link below ou can downlod a sample project if you want to play with launchers and choosers on your own.

 

The Caliburn Micro posts series

  1. The theory
  2. The first project
  3. Actions
  4. Collections and navigation
  5. Tombstoning
  6. Advanced navigation and deep links
  7. Messaging
  8. Using launchers and choosers
  9. Use your own services
  10. The Application Bar
  11. Pivot
  12. Lazy loading with pivot
Posted in Windows Phone | Tagged , , | Leave a comment