Windows Phone Pivot Performance with Caliburn Micro

07 July 2011

The in built Windows Phone pivot is great, a lovely UX. However, one needs to be careful, because when you code up a Pivot control you are effectively coding multiple “pages” at once, and all that xaml, and all that data binding is going to have a hit on performance as the number of pivots and their complexity increases.

Microsoft offers guidelines on Pivot performance which say. “Improve the performance of the pivot application by loading Pivot control content on-demand as opposed to loading everything at startup. One solution is to take the content from each PivotItem control and convert into UserControls. You can then subscribe to the LoadingPivotItem event on the root pivot control. Next, in the event handler, instantiate the proper UserControl and set it as the PivotItem content.”

Sounds like a lot of work right? Well it would be if you weren’t using Caliburn Micro (you just knew I was goign to say that right?).

Check out the pivot xaml below. This declares a simple Pivot control with two Pivot Items. Let’s say the View is called MyPivotView.xaml (and don’t forget that Caliburn Micro namespace!).

    xmlns:cm="clr-namespace:Caliburn.Micro;assembly=Caliburn.Micro"

<controls:Pivot Title="Pivot Title" x:Name="Pivot" cm:Message.Attach="[Event LoadedPivotItem] = [Action MyPivotItemLoaded($eventargs)]">
<controls:PivotItem Header="Pivot Item 1 Header" x:Name="PivotItem1Content" />
<controls:PivotItem Header="Pivot Item 2 Header" x:Name="PivotItem2Content" />
</controls:Pivot>

Now, there are 2 important things to note about the above xaml.

Firstly, the cm:Message.Attach attribute will ensure that a method called MyPivotItemLoaded(PivotItemEventArgs e) is called on the View Model automatically.

Secondly The x:Name=”PivotItem1Content” ensures that a property PivotItem1Content on your View Model can be set to an instance of a View Model (this also applies to PivotItem2Content etc.). Caliburn Micro will automagically ensure that the corresponding View for that View Model is wired up to the Content property of the Pivot Item. Neat huh?

Just create a user control for each Pivot Item’s content as the View with a corresponding View Model then Caliburn Micro will do the rest. I don’t really need to show these controls here, but for reference they would be named MyPivotItem1View.xaml and MyPivotItem2View.xaml (with corresponding MyPivotItem1ViewModel and MyPivotItem2ViewModel.cs View Model classes). To re-iterate, these are not pages, just standard user controls into which you can put anything you need.

Now let’s take a look at the page view’s View Model: MyPivotViewModel.cs.. You will firstly want to inject the ‘child’ View Models for each Pivot Item into the constructor. This is done using the Caliburn Micro bootstrapper class you already have in your application. Just set the View Models to private member variables so we can use them when we need them.

 public MyPivotViewModel(MyPivotItem1ViewModel myPivotItem1ViewModel, MyPivotItem2ViewModel myPivotItem2ViewModel)
    : base(viewModelWorker)
{
    _myPivotItem1ViewModel = myPivotItem1ViewModel;
    _myPivotItem2ViewModel = myPivotItem2ViewModel;  
}

As mentioned at the start of the article, for the conventional Caliburn Micro binding you will have a method for the Pivot Item Loading event firing and also properties to correspond to each Pivot Item’s content (see code below).

All we need to do is set the injected view models to the content properties when the pivot item loads in. This ensures the content is set as late as possible. We only need to do this once of course, so we simply check for null on each content property before setting. Note that you can control when to set the cotent, so you migth decide to set 2 at a time for example depending on requirements.

public void MyPivotItemLoaded(PivotItemEventArgs e)
{
    if (e.Item.Name == "PivotItem1Content" && this.PivotItem1Content== null)
    {
        this.PivotItem1Content= _pivotItem1ViewModel;
    }
    else if (e.Item.Name == "PivotItem2Content" && this.PivotItem2Content== null)
    {
        this.PivotItem2Content= _pivotItem2ViewModel;
    }
}

public MyPivotItem1ViewModel PivotItem1Content
{
    get { return _myPivotItem1Content; }
    set
    {
        _myPivotItem1PivotContent= value;
        NotifyOfPropertyChange(() => PivotItem1Content);
    }
}

public MyPivotItem2ViewModel PivotItem2Content
{
    get { return _myPivotItem2Content; }
    set
    {
        _myPivotItem2Content= value;
        NotifyOfPropertyChange(() => PivotItem2Content);
    }
}

What’s also nice, is that your View Model is not aware at any stage of View related classes, we’re simply working with View Models and letting Caliburn Micro do the heavy lifting.

The effects of this on the user experience are hard to overstate, so much so that this is now the default way i’m constructing Pivots in my applications.

Want to get started?

We would love to help with your next app or game, please do get in touch.