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

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

Let’s start!

Add the application bar to a project

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

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

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

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

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

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

    </Grid>

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

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

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

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

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

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

Here is the code to insert:

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

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

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

public class MainPageViewModel: Screen
{

    private string addItemText;

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

    private Uri icon;

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

    private bool isVisible;

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

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

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

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

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

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

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

 

The Caliburn Micro posts series

  1. The theory