We continue our journey to see how to use some of the most interesting features of the RadJumpList, one of the available controls in the RadControls for Windows Phone toolkit developed by Telerik.

Multi selection

Multi selection is a simple but interesting feature that will allow the user to choose more than one element from the list, by checking the checkboxes that are displayed on the left of the item. By default, selection isn’t enabled: the RadJumpList control will behave like a standard list; when the user taps on an item, you can intercept it and implement your own logic (for example, you can redirect the user to a detail page to see the details of the selected item.

The first step to enable it is to set the IsCheckModeEnabled property to True and you can do it in the XAML or in the code. Once it’s enabled, you’ll notice that the left margin of the items will be greater, but the list will continue to work as usual.

You have two ways to effectively enable multi selection mode:

  • You can tap on the left of any item: multi selection will be activated and you’ll see the checkboxes appear.
  • You can set the IsCheckModeActive property of the RadJumpList control to True: this way, you can also enable the multi select mode in the code if, for example, you want to provide a button for this purpose. The native Mail app shows an example of this behavior: you can enable multi select mode by tapping on the left of any item or by using the appropriate button in the Application Bar.

By default, if you’ve enable grouping like we’ve seen in the previous post, user will be allowed also to select groups and not only single items. If you want to disable this behavior, you need to set the GroupCheckMode to None: this way, checkboxes will be displayed only next to the single items.

Here is a sample of a RadJumpList control with multi selection enabled:

<telerikDataControls:RadJumpList x:Name="People" IsCheckModeEnabled="True" GroupCheckMode="None">
    <telerikDataControls:RadJumpList.ItemTemplate>
        <DataTemplate>
            <StackPanel>
                <TextBlock Text="{Binding Path=Name}" />
                <TextBlock Text="{Binding Path=Surname}" />
            </StackPanel>
        </DataTemplate>
    </telerikDataControls:RadJumpList.ItemTemplate>
</telerikDataControls:RadJumpList>

Managing the selected items is really simple: the RadJumpList control exposes a property called CheckedItems, which contains the collection of items that have been selected. Here is a really sample code that can be used to show the number of items that have been selected by the user.

private void OnShowItemsClicked(object sender, RoutedEventArgs e)
{
    MessageBox.Show(People.CheckedItems.Count.ToString());
}

Supporting multi selection with MVVM

One issue of the RadJumpList control is that, at the moment, CheckedItems doesn’t support binding, so you can’t have a property in your ViewModel with the collection of the items that have been selected. The best workaround I’ve found and that I’m using in my Speaker Timer app is to use a custom behavior, that has been developed by my dear friend Marco Leoncini. The purpose of this behavior is to automatically store, in a property of the ViewModel, the list of selected items.

First, you need to define in your ViewModel a collection, that will hold the items that will be checked by the user. For the following samples, I’m going to use the helpers provided by the MVVM Light Toolkit by Laurent Bugnion, that you can install using NuGet. Here is a ViewModel sample:

public class MultiSelectViewModel : ViewModelBase
{
    private ObservableCollection<Person> people;

    public ObservableCollection<Person> People
    {
        get { return people; }
        set
        {
            people = value;
            RaisePropertyChanged("People");
        }
    }

    private ObservableCollection<Person> itemsToDelete;

    public ObservableCollection<Person> ItemsToDelete
    {
        get { return itemsToDelete; }
        set
        {
            itemsToDelete = value;
            RaisePropertyChanged("ItemsToDelete");
        }
    }


    public MultiSelectViewModel()
    {
        ItemsToDelete = new ObservableCollection<Person>();
    }
}

In this sample, ItemsToDelete is the property that will store the items selected by the user (this sample is taken from my Speaker Timer app, where multi selection is enabled to delete one or more sessions). The second step is to create a new class, that will contain our behavior:

public class AddoToCheckedItemBehavior : Behavior<RadDataBoundListBox>
{
    public AddoToCheckedItemBehavior() { }

    protected override void OnAttached()
    {
        base.OnAttached();
        this.AssociatedObject.ItemCheckedStateChanged += AssociatedObject_ItemCheckedStateChanged;
    }

    void AssociatedObject_ItemCheckedStateChanged(object sender, ItemCheckedStateChangedEventArgs e)
    {
        var viewModel = ((FrameworkElement)sender).DataContext as MainPageViewModel;
        if (viewModel == null) return;
        if (e.IsChecked)
        {
            viewModel.ItemsToDelete.Add(((Telerik.Windows.Data.IDataSourceItem)e.Item).Value as Session);
        }
        else { viewModel.ItemsToDelete.Remove(((Telerik.Windows.Data.IDataSourceItem)e.Item).Value as Session); }
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();
        this.AssociatedObject.ItemCheckedStateChanged -= AssociatedObject_ItemCheckedStateChanged;
    }
}

The purpose of this behavior is very simple: it inherits from the Behavior<RadJumpList> class (so that it can be used in combination with a RadJumpList control) and it subscribes to the ItemCheckedStateChanged event, that is triggered every time the user checks or unchecks an item from the list. When this event is triggered, we get a reference to the current ViewModel and we add or remove the selected item from the ItemsToDelete collection. To do this, we use a property returned by the event handler’s parameter: the e object (which type is ItemCheckedStateChangedEventArgs) contains the properties Item (which is the selected item) and IsChecked (which is a boolean, that tells you if the item has been checked or unchecked).

There’s space for improvements in this behavior: one limitation is that it works just for our specific ViewModel, since when we get the reference to the ViewModel associated to the current view (using the DataContext property) we cast it to the MainViewModel class. If we need to apply the same behavior to multiple RadJumpList controls in different page, we can create a base class which our ViewModels can inherit from. This base class will host the the ItemsToDelete property, so that every ViewModel will be able to use it. This way, we can change the cast from MainViewModel to our new base class and reuse the same behavior for different ViewModels.

How to apply the behavior? First, we need to add a reference to the System.Windows.Interactivity library, that is available by clicking with the right click on your project and choosing Add new reference: you’ll find it in the Assemblies, Extensions sections.

After that, you can add the behavior directly in the XAML, by declaring the following namespace (plus the namespace of the behavior’s class you’ve created):

xmlns:i=”clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity”

<telerikDataControls:RadJumpList IsCheckModeEnabled="True" GroupCheckMode="None" ItemsSource="{Binding Path=People}">
    <telerikDataControls:RadJumpList.ItemTemplate>
        <DataTemplate>
            <StackPanel>
                <TextBlock Text="{Binding Path=Name}" />
                <TextBlock Text="{Binding Path=Surname}" />
            </StackPanel>
        </DataTemplate>
    </telerikDataControls:RadJumpList.ItemTemplate>
    <i:Interaction.Behaviors>
        <behaviors:AddoToCheckedItemBehavior />
    </i:Interaction.Behaviors>
</telerikDataControls:RadJumpList>

Now in the ViewModel, automatically, the ItemsToDelete property will always contain the items that have been checked by the user, so that you can use them for your own needs. In the following sample, we define a command to simply show a message with the number of selected items:

private RelayCommand showCount;

 public RelayCommand ShowCount
 {
     get
     {
         if (showCount == null)
         {
             showCount = new RelayCommand(() =>
                                              {
                                                  MessageBox.Show(ItemsToDelete.Count.ToString());
                                              });
         }
         return showCount;
     }
 }

Happy coding! As usual, remember that the sample project doesn’t contain the Telerik libraries, since we’re talking about a commercial suite.

Tagged with:  

Telerik is a very well known company by Microsoft developers, since it develops probably the best components and controls for .NET technologies.  Its relationship with Microsoft developers started a long time ago, since, in their catalogue, you can find controls for “old” technologies like Windows Forms.

It’s a commercial company: their controls suites aren’t free, even if Telerik is a company that has a strong relationships with communities, so it’s not rare to receive free licenses during events and conferences. But, recently, their Windows Phone suite has started to gain a lot of interest among Windows Phone developers for many reasons:

  • The quality of the provided controls is very high: they can make your life easier and they almost fully support binding (even when standard controls doesn’t), so they are a good companion for MVVM apps.
  • Telerik suites, usually, have a high price, because they include all the available controls developed by the company. But, since Windows Phone is gaining a lot of interest also from hobbyst and students, Telerik has decided to sell at 99$ just the Windows Phone controls, without having to buy the full suite: for this reason, the price is affordable also if you’re not a professional.
  • As I’ve already mentioned in this blog, Nokia has created a new program called Nokia Premium Developer which, at the same price of the standard Microsoft Store registration (99 $), it includes a token to create or renew a Store account, a certain number of API calls for the Buddy’s API services and, guess what, a Telerik license!

For these reasons, and due to my recent experience with the suite (I used it in my latest projects), I’ve decided to write some blog posts to show you how to use some of the controls that are part of the toolkit and how they can improve your work.

Le’t start with the JumpList!

The JumpList control

Lists are one of the key part of every Windows Phone application: it’s almost impossible to find an application that doesn’t need to display a collection of data. Originally Windows Phone 7 included just the ListBox control for this scenario, that had many limitations and performance problems. Things are changed now, since Windows Phone 8 includes a control called LongListSelector (that, previously, was part of the Phone Toolkit), that has much better performances and that supports features like grouping and sorting.

But things are still not perfect: the LongListSelector doesn’t support features that are, instead, widely used by the platform itself, like sticky headers or multiple selection. Plus, the approach to group your data is not very simple: it requires to create a separate class to manage them and to change your collection, like it’s described in this MSDN article.

For this reason Telerik has included in the RadControls for Windows Phone suite a control called RadJumpList, which goal is to overcome the limitations of the original LongListSelector control. Let’s see how to implement some scenarios with the RadJumpList control.

Grouping data

Grouping is one of the features that is widely used also by the native operating system: just think at the People hub, where contacts are grouped by their initial, so that the user can easily jump from a letter to another. The RadJumpList control makes very easy to group your data in the same way and, most important, it doesn’t require you to change your original collection: with the LongListSelector control, in fact, instead of just setting your collection as ItemsSource property of the control, you have first to group your data. With RadJumpList, instead, you can keep using your original collection, where items are not grouped: grouping will be added in a second step. Let’s start with a simple example: we have a collection of people, that we want to display in the application. Every person is mapped with a specific class:

public class Person
{
    public string Name { get; set; }
    public string Surname { get; set; }
    public string City { get; set; }
}

Let’s start by adding the control in the XAML (you’ll need to declare the following namespace xmlns:telerikDataControls=”clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.Data” in the PhoneApplicationPage node in the XAML).

<telerikDataControls:RadJumpList x:Name="People">
    <telerikDataControls:RadJumpList.ItemTemplate>
        <DataTemplate>
            <StackPanel>
                <TextBlock Text="{Binding Path=Name}" />
                <TextBlock Text="{Binding Path=Surname}" />
            </StackPanel>
        </DataTemplate>
    </telerikDataControls:RadJumpList.ItemTemplate>
</telerikDataControls:RadJumpList>

As you can see, for the basic use there are no differences with a standard ListBox or LongListSelector: we define an ItemTemplate, which is the template that will be used to show every item of the collection. In this sample, we simply display the Name and Surname properties of the person.

In the code behind, we’re going to load some data as we’ve already used to do with a ListBox:

public MainPage()
{
    InitializeComponent();

    // Sample code to localize the ApplicationBar
    //BuildLocalizedApplicationBar();

    ObservableCollection<Person> people = new ObservableCollection<Person>
        {
            new Person
                {
                    Name = "Matteo",
                    Surname = "Pagani",
                    City = "Como"
                },
            new Person
                {
                    Name = "Ugo",
                    Surname = "Lattanzi",
                    City = "Milan"
                },
            new Person
                {
                    Name = "Mario",
                    Surname = "Rossi",
                    City = "Milan"
                }
        };

    People.ItemsSource = people;
}

Nothing special here: if you launch the application, you’ll see the list of people displayed in the RadJumpList control.

But if we want grouping? We need to interact with the GroupDescriptors property of the RadJumpList control, that can be used to set how we want to group our data. Let’s say we want to group our contacts by city. Here is the code we need to add before setting the ItemsSource property of the RadJumpList control.

GenericGroupDescriptor<Person, string> group = new GenericGroupDescriptor<Person, string>();
group.KeySelector = (person) =>
    {
        return person.City;
    };

People.GroupDescriptors.Add(group);

Easy, isn’t it? We just need to define a new GenericGropDescriptor<T, Y> object, where T is the base class of our collection (in this case, Person), while Y is the property’s type we want to use for grouping (in our sample, City, that is a string). Then we assign a function to the KeySelector property, that is the way we want to group our data. This function features, as parameter, the object’s type of our collection (in our case, Person), so we simply need to return, as result, the field we want to use for grouping (City). In the end, we add the GenericGroupDescriptor object to the GroupDescriptors collection of the RadJumpList control. And that’s it! Now launch the application and you’ll see your data grouped by city: plus, the group headers will be interactive, so that the user can tap on them and jump directly to the selected group.

jumplist1jumplist2

 

Sorting

Sorting your data is really easy and RadJumpList supports two way of sorting: by group and by item. The two things can be combined, so that you can have your groups ordered with a criteria and the items inside that group ordered for another one. Sorting the groups is really easy: the GenericGroupDescriptor class we’ve just seen supports a property called SortMode, that accepts one of the values of the ListSortMode enumerator: Ascending, Descending and None.

GenericGroupDescriptor<Person, string> group = new GenericGroupDescriptor<Person, string>();
group.SortMode = ListSortMode.Ascending;
group.KeySelector = (person) =>
    {
        return person.City;
    };

People.GroupDescriptors.Add(group);

This type of sorting applies just to the order in which groups are displayed in the list and it doesn’t affect the elements inside every group: if you try to change, for example, the SortMode to ListSortMode.Descending, you’ll see that the group labeled Milan will be displayed before the one labeled Como, but the order of the contacts inside every group won’t change.

If you want to apply a specific order also for the contacts, you’ll have to use a GenericSortDescriptor<T, Y> object, that works exactly like the GenericGroupDescriptor one. The only difference is that, in this case, as value of the KeySelector property we will pass a function that returns the property we want to use to order the items.

GenericSortDescriptor<Person, string> sort = new GenericSortDescriptor<Person, string>();
sort.SortMode = ListSortMode.Ascending;
sort.KeySelector = person =>
    {
        return person.Name;
    };

People.SortDescriptors.Add(sort);

In this sample we want to order the contacts by name: after we’ve set up our GenericSortDescriptor object, we add it to another special collection exposed by the RadJumpList control, that is called SortDescriptors. This sorting is independant from the grouping one: if you try to change the SortMode to ListSortMode.Descending you’ll notice that the groups order won’t change, but the order of the contacts inside every group will be different.

Grouping and sorting with MVVM

Using grouping and sorting in an application built with the MVVM pattern it’s easy, since the RadJumpList control exposes two properties called GroupDescriptorsSource and SortDescriptorsSource, that support binding. They accept a collection as input, so you’ll have to create in your ViewModel two collections to hold the GenericGroupDescriptor or GenericSortDescriptor criterias you want to apply (even if you’re going to add just one criteria).

Here is a sample of a ViewModel:

public class MainViewModel: INotifyPropertyChanged
{
    private List<GenericGroupDescriptor<Person, string>> groupDescriptors;

    public List<GenericGroupDescriptor<Person, string>> GroupDescriptors
    {
        get { return groupDescriptors; }
        set
        {
            groupDescriptors = value;
            OnPropertyChanged("GroupDescriptors");
        }
    }

    private List<GenericSortDescriptor<Person, string>> sortDescriptors;

    public List<GenericSortDescriptor<Person, string>> SortDescriptors
    {
        get { return sortDescriptors; }
        set
        {
            sortDescriptors = value;
            OnPropertyChanged("SortDescriptors");
        }
    }

    public MainViewModel()
    {
        GroupDescriptors = new List<GenericGroupDescriptor<Person, string>>();
        SortDescriptors = new List<GenericSortDescriptor<Person, string>>();

        GenericGroupDescriptor<Person, string> group = new GenericGroupDescriptor<Person, string>();
        group.SortMode = ListSortMode.Ascending;
        group.KeySelector = (person) =>
            {
                return person.City;
            };

        GroupDescriptors.Add(group);

        GenericSortDescriptor<Person, string> sort = new GenericSortDescriptor<Person, string>();
        sort.SortMode = ListSortMode.Ascending;
        sort.KeySelector = person =>
            {
                return person.Name;
            };

        SortDescriptors.Add(sort);
    }

    public event PropertyChangedEventHandler PropertyChanged;

    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

As you can see, we simply use the same code we’ve seen before but, instead of adding the generic descriptors directly to the RadJumpList control, we add them to two collections exposed by the ViewModel. Then, in the XAML, we use binding to connect them to the RadJumpList control:

<telerikDataControls:RadJumpList x:Name="People" GroupDescriptorsSource="{Binding Path=GroupDescriptors}" SortDescriptorsSource="{Binding Path=SortDescriptors}">
    <telerikDataControls:RadJumpList.ItemTemplate>
        <DataTemplate>
            <StackPanel>
                <TextBlock Text="{Binding Path=Name}" />
                <TextBlock Text="{Binding Path=Surname}" />
            </StackPanel>
        </DataTemplate>
    </telerikDataControls:RadJumpList.ItemTemplate>
</telerikDataControls:RadJumpList>

In the next posts

In the next posts we’ll see some other features of the RadJumpList control: how to use sticky headers, multiple selection and how to customize grouping. Meanwhile, you can play with the sample project (obviously, the Telerik libraries are missing, since they can’t be redistributed, you’ll need to manually add your own ones).

Tagged with:  

I recently published a big update for an application I’ve developed a long time ago and that I “abandoned” until today: Speaker Timer. It’s a simple application for speakers, that can be used to track the time of your speech and to immediately see if you’re exceeding or not the time you’ve been assigned.

During the development I’ve encountered some issues to correctly manage the phone’s theme: as you can see from the screenshot on the Store, the application has a blue background and white texts. For this reason, everything works fine when the app is used with the dark theme but, if the user is using the light one, things start to get messy: texts starts to get hard to read and the overall user experience is really bad. In the following screenshots you can see, on the left, the application with the dark theme and, on the right, with the light theme: as you can see, since texts are in black over a blue background, they aren’t easy to read.

session2session1

Luckily Jeff Wilcox, that works in Microsoft as leader of the Windows Azure SDK and many other open source projects, has developed a really great library called PhoneThemeManager, that is described in this blog post. The purpose of this library is really simple: with one line of code you’re able to force a specific theme in your application so that, if your application looks bad with another theme because you’ve used custom colors, you can force all the elements in the page to use your own theme.

Once you’ve installed the library using NuGet, you’ll just have to use the singleton ThemeManager class when your application starts (it can be inside the App.xaml.cs constructor or, for example, since Speaker Timer is built using Calibur Micro, I used it in the OnStartup() method of the boostrapper).

This class exposes some methods to change the theme: the most important ones are ToDarkTheme() and ToLightTheme(), that will override every color and force all the elements to use the theme that matches best with your application. In my case, since Speaker Timer looks bad with a light theme, I’ve forced the dark theme by including the following code:

public App()
{
    // Standard Silverlight initialization
    InitializeComponent();

    ThemeManager.ToDarkTheme();

    // Show graphics profiling information while debugging.
    if (System.Diagnostics.Debugger.IsAttached)
    {
        // Display the current frame rate counters.
        Application.Current.Host.Settings.EnableFrameRateCounter = false;

        // Show the areas of the app that are being redrawn in each frame.
        //Application.Current.Host.Settings.EnableRedrawRegions = true;

        // Enable non-production analysis visualization mode, 
        // which shows areas of a page that are being GPU accelerated with a colored overlay.
        //Application.Current.Host.Settings.EnableCacheVisualization = true;
    }
}

The result, with this simple code, is that, regardless of the theme chosen by the user, the application will always look like in the first screenshot.

Managing a custom Application Bar

As I’ve already mentioned, Speaker Timer is developed using Caliburn Micro as a MVVM Framework: for this reason, I’m not using the native Application Bar, but the one I’ve talked about in this post, that supports binding and Caliburn Micro conventions. However, by using this bar I’ve found an issue connected to the user theme: even if I’ve forced the dark theme using Jeff Wilcox’s library, the application bar was still using the light theme. It’s not a big deal, but the biggest issue was that, when I moved to the other pages and then I came back to the first one, the Application Bar suddendly changed and became dark. This happens because the PhoneThemeManager library can’t immediately ovveride the Application Bar colors, so you can see the change only at a second time.

How to fix this situation? The library exposes two methods for this scenario: the first one is called CreateApplicationBar() and can be used to create a new application bar with the proper theme. This method is useful in case you’re creating and managing the Application Bar in the code behind and not in the XAML. But this is not my case, since Speaker Timer is built using MVVM and the custom application bar is added directly in the XAML. Here comes another method offered by the library, called MatchOverriddenTheme(): it’s an extension method applied to the IApplicationBar interface, so every custom Application Bar implemention should support it. When you call this method, the application bar colors are immediately overwritten, so you don’t have to deal with the graphic glitch I’ve mentioned before.

There’s only one important thing to keep in mind: this method should be called in the Loaded event of every page with an application bar and not in the page costructor, otherwise it won’t work properly, as you can see in the following screenshot.

glitch

So, the first operation is to give a Name to your custom application bar using the x:Name property, like in the following sample:

<bindableAppBar:BindableAppBar x:Name="AppBar">
    <bindableAppBar:BindableAppBarButton Text="{Binding Source={StaticResource LocalizedStrings}, Path=LocalizedResources.Add}" IconUri="/Assets/Icons/Add.png" 
                                         x:Name="NewSession" 
                                         Visibility="{Binding Path=IsCheckModeEnabled, Converter={StaticResource NegativeBooleanToVisibilityConverter}}" />

    <bindableAppBar:BindableAppBarButton Text="{Binding Source={StaticResource LocalizedStrings}, Path=LocalizedResources.Select}" IconUri="/Assets/Icons/Selection.png"
                                         x:Name="Selection"
                                         Visibility="{Binding Path=IsCheckModeEnabled, Converter={StaticResource NegativeBooleanToVisibilityConverter}}" />
</bindableAppBar:BindableAppBar>

 

Then, in the code behind, you should subscribe to the Loaded event and call the MatchOverriddenTheme() on the custom application bar, like in the following sample:

public partial class MainPage : PhoneApplicationPage
{
    // Constructor
    public MainPage()
    {
        InitializeComponent();
        Loaded += (obj, args) =>
            {
                AppBar.MatchOverriddenTheme();
            };
    }
}

That’s all! Happy coding!

Tagged with:  

When I started to play with Windows Store apps development for Windows 8, one of the things I liked most was the base LayoutAwarePage class, from which every page inherits from. This class introduces a built in support for managing the different visual states of the page using the VisualStateManager: every time the page state changes (Portrait, Landscape, Snapped or Filled), a specific state is triggered so, as a developer, it’s really easy to apply animation and changes the layout of the page.

Unfortunately, this built in helper is missing in Windows Phone, so you have to manually manage the orientation from portrait to landscape or vice versa: Windows Phone is able to, automatically, arrange the controls when the orientation is changed, but often isn’t enough because you want to deeply change the layout of the page according to the orientation.

So I googled a little bit and I’ve found that a talented developer that has a blog called Tomahawk’s blog (I wasn’t able to find the real developer name, if you contact me I’ll be more than happy to update the post) has developed a really nice extension, that can be used to recreate the same approach I’ve described in Windows 8 in both Windows Phone 7 and 8 applications.

Let’s see how to use it. Unfortunately, the library isn’t available on NuGet, so you’ll have to download the source project and compile it: you’ll get, in the end, different DLLs, one for each supported platform. For your convenience, in the sample project attached at the end of this post you’ll find the already precompiled libraries.

After you’re created your project, the first modify to do to start supporting orientation is to change the SupportedOrientations property in the XAML: you’ll find it as a property of the PhoneApplicationPage object in XAML, which is the main control that identifies the page. This property tells to the page which orientations are you going to support and, by default, it’s set to Portrait, so the page doesn’t react when the device is rotated in landscape. To enable it, you’ll have to change the value in PortraitOrLandscape.

The second thing is to register, always in the PhoneApplicationPage control, the library: you’ll have to register the namespace of the library (which, for a Windows Phone 8 application is xmlns:orientationHelper=”clr-namespace:OrientationHelper;assembly=OrientationHelper.PhoneRT” while for a Windows Phone 7 one is  xmlns:orientationHelper=”clr-namespace:OrientationHelper;assembly=OrientationHelper.WP7″) and set the property OrientationHelper.IsActive to true.

Here is how it will look like, in the end, the PhoneApplicationHelper declaration of your page:

<phone:PhoneApplicationPage
    x:Class="Orientation.MainPage"
    SupportedOrientations="PortraitOrLandscape" Orientation="Portrait"
    orientationHelper:OrientationHelper.IsActive="True"
    >

Once you’ve completed this steps, automatically the application will start to apply different visual states every time the orientation changes. So, you’ll need to define them in your page, inside the main Grid of the application (the one that is called LayoutRoot in the standard template).

Here is a sample:

<VisualStateManager.VisualStateGroups>
    <VisualStateGroup x:Name="PageOrientationStates">
        <VisualState x:Name="Landscape" />
        <VisualState x:Name="LandscapeLeft" />
        <VisualState x:Name="LandscapeRight" />

        <VisualState x:Name="Portrait" />
        <VisualState x:Name="PortraitUp" />
        <VisualState x:Name="PortraitDown" />
    </VisualStateGroup>
</VisualStateManager.VisualStateGroups>

As you can see, we’ve added a VisualStateGroup which name is PageOrientationStates and, inside it, there’s a specific VisualState for every possible orientation that can be intercepted by the application. This XAML is basically useless: if you use an empty VisualState tag, like in this sample, without specifying anything inside, no changes will be applied and the standard layout of the controls will be used.

What we’re going to do is, inside every VisualState, specifying one or more animations, that will tell to our controls how they should behave or look in portrait or landscape mode: we can change any property of any control, so we can hide them, move them, change alignment or orientation, etc.

Changing value of a property

In this sample we’re going to see a simple animation, that it’s often used because it can be used to simply change the value of a property: it’s perfect if you need to change visibility, alignment, orientation, etc.

We’ll start from a very simple XAML:

<StackPanel x:Name="MainPanel">
    <TextBlock Text="Text 1" />
    <TextBlock Text="Text 2" />
    <TextBlock Text="Text 3" />
    <TextBlock Text="Text 4" />
</StackPanel>

We have four TextBlock controls, inside a StackPanel, so they are displayed one below the other. We want that, in landscape mode, the Orientation property of the panel is changed to Horizontal and that the content is aligned at the center of the page.

The first thing to do is to assign a name to every control we want to change during the animation: in this sample, we’ve assigned the name MainPanel to the StackPanel control, since we want to manipulate its properties.

Here is the VisualStateManager definition to accomplish our task:

<VisualStateManager.VisualStateGroups>
    <VisualStateGroup x:Name="PageOrientationStates">
        <VisualState x:Name="Landscape">
            <Storyboard>
                <ObjectAnimationUsingKeyFrames Storyboard.TargetName="MainPanel" Storyboard.TargetProperty="Orientation">
                    <DiscreteObjectKeyFrame KeyTime="0" Value="Horizontal" />
                </ObjectAnimationUsingKeyFrames>
                <ObjectAnimationUsingKeyFrames Storyboard.TargetName="MainPanel" Storyboard.TargetProperty="HorizontalAlignment">
                    <DiscreteObjectKeyFrame KeyTime="0" Value="Center" />
                </ObjectAnimationUsingKeyFrames>
            </Storyboard>
        </VisualState>
        <VisualState x:Name="LandscapeLeft">
            <Storyboard>
                <ObjectAnimationUsingKeyFrames Storyboard.TargetName="MainPanel" Storyboard.TargetProperty="Orientation">
                    <DiscreteObjectKeyFrame KeyTime="0" Value="Horizontal" />
                </ObjectAnimationUsingKeyFrames>
                <ObjectAnimationUsingKeyFrames Storyboard.TargetName="MainPanel" Storyboard.TargetProperty="HorizontalAlignment">
                    <DiscreteObjectKeyFrame KeyTime="0" Value="Center" />
                </ObjectAnimationUsingKeyFrames>
            </Storyboard>
        </VisualState>
        <VisualState x:Name="LandscapeRight">
            <Storyboard>
                <ObjectAnimationUsingKeyFrames Storyboard.TargetName="MainPanel" Storyboard.TargetProperty="Orientation">
                    <DiscreteObjectKeyFrame KeyTime="0" Value="Horizontal" />
                </ObjectAnimationUsingKeyFrames>
                <ObjectAnimationUsingKeyFrames Storyboard.TargetName="MainPanel" Storyboard.TargetProperty="HorizontalAlignment">
                    <DiscreteObjectKeyFrame KeyTime="0" Value="Center" />
                </ObjectAnimationUsingKeyFrames>
            </Storyboard>
        </VisualState>

        <VisualState x:Name="Portrait" />
        <VisualState x:Name="PortraitUp" />
        <VisualState x:Name="PortraitDown" />
    </VisualStateGroup>
</VisualStateManager.VisualStateGroups>

The animation we’re going to use is called ObjectAnimationUsingKeyFrames, that can be used to change the control’s properties and to specify the exact time of the animation, so that we can control it. In our case, we don’t want a real animation, but just to change the value. For this reason, every animation will have just one frame (identified by the ObjectAnimationUsingKeyFrames object) with the property KeyTime set to 0 (so that the animation will trigger immediately). The ObjectAnimationUsingKeyFrames object is placed inside the object that identifies the real animation, which is called ObjectAnimationUsingKeyFrames: using two attached properties we’re going to set which is the control that we want to animate (Storyboard.TargetName) and which is the property we want to change (Storyboard.TargetProperty). In this sample we’re going to apply two animations: the first one will change the Orientation property, the second one the HorizontalAlignment one. The new value that should be assigned is set as Value property of the DiscreteObjectKeyFrame object. In case the device is rotated in landscape mode, we change the StackPanel’s Orientation to Horizontal and the HorizontalAlignment to Center.

We repeat this for every VisualState that identifies a landscape orientation: Landscape, LandscapeLeft and LandscapeRight. We leave, instead, the portrait VisualStates empty: this way, when the device is rotated back in portrait, the standard layout is restored. This is a good shortcut to avoid creating other animations simply to put back the controls in the original state.

This kind of animation can be used also if you want to totally change the layout when the orientation is changed: since, in these cases, moving, hiding or aligning too many controls can be complex, it’s easier to create two different layouts (inside, for example, two different Grid or StackPanel controls) and, by changing the Visibility property of the panel, hiding or displaying the proper one. This approach works better if you’re developing the application using the MVVM pattern, since you can’t assign the same x:Name property value to two controls. Using binding, instead, you’re able to assign the same property to multiple controls without problems.

object1object2

Moving objects with animations

Another common situation is to move controls with an animation, to change the layout. In this case we can use a DoubleAnimation, which can be used to change one of the properties that has a numeric value, like the width, the height or the font size. To move a control in the page, we need to use a transformation, that are a way to change the layout of the control without having to redefine every property. For example, if we want to move an object in the page we can use a TranslateTransform, that supports changing the X and Y coordinates of the control. We’ll start from a situation similar to the previous sample: we’ have a StackPanel with some TextBlock controls inside.

The first thing is to apply a TranslateTrasform to the StackPanel, so that we can use it in the animation to move the control.

<StackPanel>
    <StackPanel.RenderTransform>
        <TranslateTransform x:Name="PanelTranslate"/>
    </StackPanel.RenderTransform>
    <TextBlock Text="Text 1" />
    <TextBlock Text="Text 2" />
</StackPanel>

In this case, we don’t give a value to the X and Y property of the TranslateTransform object, since we want to keep it in the original position when it’s loaded. We give it just a name, because we’ll need to interact with it when the device is rotated in landscape mode. Here is how we define our VisualStateManager:

<VisualStateManager.VisualStateGroups>
    <VisualStateGroup x:Name="PageOrientationStates">
        <VisualState x:Name="Landscape">
            <Storyboard>
                <DoubleAnimation Storyboard.TargetName="PanelTranslate" Storyboard.TargetProperty="X" To="400" />
            </Storyboard>
        </VisualState>
        <VisualState x:Name="LandscapeLeft">
            <Storyboard>
                <DoubleAnimation Storyboard.TargetName="PanelTranslate" Storyboard.TargetProperty="X" To="400" />
            </Storyboard>
        </VisualState>
        <VisualState x:Name="LandscapeRight">
            <Storyboard>
                <DoubleAnimation Storyboard.TargetName="PanelTranslate" Storyboard.TargetProperty="X" To="400" />
            </Storyboard>
        </VisualState>

        <VisualState x:Name="Portrait" />
        <VisualState x:Name="PortraitUp" />
        <VisualState x:Name="PortraitDown" />
    </VisualStateGroup>
</VisualStateManager.VisualStateGroups>

For every VisualState that refers to a landscape position, we set a DoubleAnimation. Also in this case we need to set which is the control we want to animate (the TranslateTransform object) and which is the property we want to change (the X position). Plus, we define the ending value of the animation using the To property: in this sample, we’re telling that the control should move from the starting position to the coordinate 400 of the X axe.

This kind of approach can be useful when you have some controls which position needs to be rearranged when the device is rotated.

translation1translation2

In the end

This is just one of the available approaches to manage orientations. Another good solution is to use behaviors, like the MVP Andràs Velvàrt explains in his blog post. Personally I like more the approach I’ve described in this post because is similar to the one offered by Windows 8 and Windows Store apps, but it’s up to you to choose what you like best!

Tagged with:  

This post is simply a collector of all the blog posts I wrote about using Caliburn Micro, a very powerfull Model-View-ViewModel framework, in Windows Phone 8. The posts are listed in chronological order, so it will be easier for you to follow the logic path of the posts: from the basic concepts (what’s Caliburn, conventions, etc.) to the most complex stuff (tombstoning, collections, messaging).

This list will be published also at the bottom of every post of the series, so that it will be easier for you to jump from a post to another.

Here we go!

  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
Tagged with:  

I’m working on a big update for my Speaker Timer application and one of the features I’m implementing is Skydrive’s support, to create a backup of the data and restore it in case the user buys a new phone or he has to reset it. There are many tutorials on the web about this scenario, but I think that all of them are missing some crucial information: some tutorials only explain how to upload files on Skydrive and not how to download them; the official documentation explains how to execute a download, but it mentions stuff like “file id” without explaining in details how to get it.

For this reason  I’ve decided to write a tutorial on my own: I hope you’ll find it useful for your applications. Please keep in mind that this tutorial is based on Windows Phone 7: on Windows Phone 8 the approach is really similar, but the Live SDK supports async methods instead of using the callback pattern.

Prepare the environment

The first thing to do is to add the Live SDK to your project, which is the SDK provided by Microsoft to interact with the Live services. You can use it to identify the user, to get some information about it, to access to his remote pictures and so on. One of the supported feature is Skydrive’s access: you can read and write files from the user’s cloud storage. You can download the Live SDK for Windows Phone from the official website or you can simply add it using NuGet.

The second step is to register your application in the Live Developer Portal: this step is required to get access to the Client Id, which is a unique identifier needed by the Live services to identify your application. Once you’ve logged in with your Microsoft Account on the portal click on the My apps section and choose the option Create application.

image

In the first step you have to specify a unique identifier (in the Application Name field) and the primary language of your application. Once you’re ready, click on the I accept button. In the second and final step the only option you do is to specify that you’re developing a mobile application, by choosing Yes in the Mobile client app option. Remember to press Save to confirm.

image

In this page you’ll also get the Client ID, that we’re going to use soon to configure our application.

Sign in with the Microsoft Account

The first operation to do in your application is to add sign in support: the user will have to login to his Microsoft Account before being able to interact with Skydrive. This operation is really simple, since the Live SDK includes a built in control that takes care of all the process. The first thing is to add, in the PhoneApplicationPage declaration in the XAML, the namespace that contains the control:

xmlns:my=”clr-namespace:Microsoft.Live.Controls;assembly=Microsoft.Live.Controls”

Now you are able to add the control in the page, like in the following sample:

<my:SignInButton ClientId="your_client_id" 
    Scopes="wl.signin wl.skydrive_update" 
    Branding="Skydrive" 
    TextType="SignIn" 
    SessionChanged="SignInButton_OnSessionChanged" 
    VerticalAlignment="Top"
/>

The control is simply a button, that will take care of the all operations needed to do the login: when the user taps on it, a web view is opened, asking for the Microsoft account credentials. Once the login process is completed, in the application you’ll get the information if the login operation is successful or not.

imageimage

The name of the control is SignInButton and it offers many properties to customize it. The most important one is ClientId, which contains the unique identifier of the application that you received after registering your application on the developer portal. One another important option is Scopes, that can be used to specify which features of the Live platform you’re going to use (you can find a list of all the available scopes in this page): in this case we just need wl.signin (that is the basic one, needed to support authentication) and wl.skydrive_update (which is needed to get read and write access to Skydrive). If you want to customize the look & feel of the button you can use the Branding and TextType options: the first one is used to choose the logo that will be displayed on the button (since we’re going to interact with Skydrive, we use the Skydrive option), the second one to choose the login / logout texts that will be used as label (you can also customize them using the SignInText and SignOutText properties).

Now it’s time to write some code: the button offers an event called SessionChanged, that is invoked when the user is interacting with the control and he’s trying to login. Here is how to manage the event in the code:

private void SignInButton_OnSessionChanged(object sender, Microsoft.Live.Controls.LiveConnectSessionChangedEventArgs e)
{
    if (e.Status == LiveConnectSessionStatus.Connected)
    {
        client = new LiveConnectClient(e.Session);
        MessageBox.Show("Connected!");
    }
}

The e parameter contains the status of the operation: with the Status property (which type is LiveConnectSessonStatus) we are able to know if the login operation completed successfully (LiveConnectSession.Status.Connected). In this case, we can create a new instance of the LiveConnectClient class, which is the base class needed to perform all the operations with the Live services. As parameter you have to pass the identifier of the current session, which is stored in the Session property of the method’s parameters.

Backing up the file

Now that the user is logged in and you have a valid LiveConnectClient object, you’re ready to backup your files on Skydrive. In this sample, we’re going to save a single file in the Skydrive’s root:

private void OnBackupClicked(object sender, RoutedEventArgs e)
{
    client.UploadCompleted += client_UploadCompleted;
    using (IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForApplication())
    {
        IsolatedStorageFileStream stream = storage.OpenFile("Sessions.xml", FileMode.Open);
        client.UploadAsync("me/skydrive", "SpeakerTimer.bak", stream, OverwriteOption.Overwrite);
    }
}

void client_UploadCompleted(object sender, LiveOperationCompletedEventArgs e)
{
    if (e.Error == null)
    {
        MessageBox.Show("Upload successfull!");
    }
}

The first thing we do is to subscribe to the UploadCompleted event exposed by the LiveConnectClient class, which is invoked when the upload operation is completed: this is needed because the operation is asynchronous. Then, using the storage APIs, we get the stream of the file we want to backup: in my sample, it’s a file called Sessions.xml stored in the isolated storage’s root. In the end we start the upload operation by calling the UploadAsync() method of the client, which accepts:

  • The path where to save the file on the Skydrive’s storage. With using the “me/skydrive” syntax we’re going to save the file in the Skydrive’s root;
  • The name of the file that will be saved on Skydrive;
  • The stream of the file to save: it’s the one that we’ve retrieved using the OpenFile() method of the IsolatedStorageFile class;
  • What to do in case the file on Skydrive already exists: in our sample we simply overwrite it by using the OverwriteOption.Overwrite value.

When the UploadCompleted event is raised we simply check if an error has occurred, by checking the value of the Error property of the LiveOperationCompletedEventArgs parameter: in case it’s null, we show a message with the notification that the upload has completed successfully. If we did everything correct, we’ll find the file in our Skydrive’s storage.

Restoring the file

The backup operation was very simple to accomplish; the restore operation, instead, is a little bit trickier, because every file and folder on Skydrive is identified by a unique id and you can’t access to the file we’ve uploaded using its name. One way to get it is using the parameters returned by the UploadCompleted method, which contain, in the Result property, a dictionary with all the file’s properties, so by using the following code we are able to retrieve it.

void client_UploadCompleted(object sender, LiveOperationCompletedEventArgs e)
{
    if (e.Error == null)
    {
        string fileId = e.Result["id"].ToString();
        MessageBox.Show("Upload successfull!");
    }
}

The problem with this approach is that, before getting the id of the file, we need to upload it. The most common scenario, instead, is that the user has made a reset or purchased a new phone and he wants to restore the backup after reinstalling the application: in this case no upload operations have been made, so we need to find another way to get the id of the file. The solution is to use the GetAsync() method of the client, that can be used to retrieve all the Skydrive files and folders and access to their properties. We’re going to get a list of all the Skydrive files and folders and, by exploring their properties, we’re going to find the id of the file which name is SpeakerTimer.bak.

private void OnRestoreClicked(object sender, RoutedEventArgs e)
{
    string id = string.Empty;
    client.GetCompleted += (obj, args) =>
    {
        List<object> items = args.Result["data"] as List<object>;
        foreach (object item in items)
        {
            Dictionary<string, object> file = item as Dictionary<string, object>;
            if (file["name"].ToString() == "SpeakerTimer.bak")
            {
                id = file["id"].ToString();
            }
        }
    };

    client.GetAsync("me/skydrive/files");
}

The GetAsync() method of the client accepts as parameter the path we want to explore: by using the me/skydrive/files syntax we get the list of all the files and folders inside the root. Since the method is asynchronous, we subscribe to the GetCompleted event, which is raised when the operation is done. The Result property of the parameter contains a collection of all the available files and folders, inside the item identified by the Data collection (it’s a dictionary). Since it’s a collection, we need to do a cast to the List<object> type. Every object inside this collection is another Dictionary<string, object>, that contains all the properties of the file or folder. By using a foreach we iterate over all the files and folders and, for each one, we check the value of the name’s property: if the value is the one we’re expecting (SpeakerTimer.bak), we get the id property and we store it (it will be something like file.8c8ce076ca27823f.8C8CE076CA27823F!129).

Now we’re ready to execute the real download operation:

client.DownloadCompleted += (o, a) =>
                                {
                                    Stream stream = a.Result;
                                    using (IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForApplication())
                                    {
                                        using (
                                            IsolatedStorageFileStream fileToSave = storage.OpenFile("Sessions.xml", FileMode.Create,
                                                                                                    FileAccess.ReadWrite))
                                        {
                                            stream.CopyTo(fileToSave);
                                            stream.Flush();
                                            stream.Close();
                                        }
                                    }
                                };

client.DownloadAsync(string.Format("{0}/content", id));

We use the DownloadAsync() method of the client, passing as parameter the id of the file we’ve just retrieved. It’s really important to add the /content suffix to the id: many tutorials are missing this information, but it’s crucial because, otherwise, you’ll get the JSON with all the file’s properties instead of the real content.

Since, as usual, the operation is asynchronous, we subscribe to the DownloadCompleted event, that is invoked when the download is completed and we have access to the downloaded file, which is stored, as a stream, in the Result property of the method’s parameters. By using, again, the storage APIs, we save the stream we’ve downloaded in the Isolated Storage, by creating a new file (using the OpenFile() method and passing FileMode.Create as option) and by copying the downloaded stream in the local stream.

Here is the full restore method, where the two operations are executed at the same time:

private void OnRestoreClicked(object sender, RoutedEventArgs e)
{
    string id = string.Empty;
    client.GetCompleted += (obj, args) =>
    {
        List<object> items = args.Result["data"] as List<object>;
        foreach (object item in items)
        {
            Dictionary<string, object> file = item as Dictionary<string, object>;
            if (file["name"].ToString() == "SpeakerTimer.bak")
            {
                id = file["id"].ToString();
            }
        }

        client.DownloadCompleted += (o, a) =>
                                        {
                                            Stream stream = a.Result;
                                            using (IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForApplication())
                                            {
                                                using (
                                                    IsolatedStorageFileStream fileToSave = storage.OpenFile("Sessions.xml", FileMode.Create,
                                                                                                            FileAccess.ReadWrite))
                                                {
                                                    stream.CopyTo(fileToSave);
                                                    stream.Flush();
                                                    stream.Close();
                                                }
                                            }
                                        };

        client.DownloadAsync(string.Format("{0}/content", id));
    };

    client.GetAsync("me/skydrive/files");
}

Conclusion

This is a simple solution to backup and restore your data in your application: there’s room for improvement (for example, you can encrypt the file before uploading it), but this tutorial covers pretty much all of the basic steps. Have fun!

Tagged with:  

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 Panorama, 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.

Tagged with:  

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 posts 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!

Tagged with:  

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.

Tagged with:  

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

Tagged with: