Solved: Why Don’t ApplicationBar Bindings Work? – Windows Phone 7 SDK

comments

One of the subtleties I've found recently while working with the Windows Phone 7 SDK is found when working with the ApplicationBar programmatically. There are a number of differences that the ApplicationBar has when compared to a normal Windows Phone 7 Silverlight control – these very differences can be really frustrating if you are not aware of them as they stop you from interacting with it in the same way you do other Silverlight controls. Hopefully after we’ve taken a closer look it will make sense why they are so.

imageWhat on Earth are you talking about my dear fellow?

The ApplicationBar in a Windows Phone 7 application is not, although not at first clear, a normal Silverlight control. The ApplicationBar in a Windows Phone 7 application is actually a native part of the phone’s UI that is handled by the phone’s OS independantly of your application.

This is confusing as when you open the XAML for your applications page you see at the bottom of your page’s code reference to the ApplicationBar as if it were something within the realms of your coding empire (straight from a default “Portrait Page”):

<phone:PhoneApplicationPage.ApplicationBar>
    <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True">
        <shell:ApplicationBarIconButton IconUri="/Images/appbar_button1.png" Text="Button 1"/>
        <shell:ApplicationBarIconButton IconUri="/Images/appbar_button2.png" Text="Button 2"/>
        <shell:ApplicationBar.MenuItems>
            <shell:ApplicationBarMenuItem Text="MenuItem 1"/>
            <shell:ApplicationBarMenuItem Text="MenuItem 2"/>
        </shell:ApplicationBar.MenuItems>
    </shell:ApplicationBar>
</phone:PhoneApplicationPage.ApplicationBar>

The above at leads you to believe that the ApplicationBar is actually simply a control in your applications page – but this assumption would be incorrect.

This becomes even more clear when you try and interact with the controls in your ApplicationBar using code. In my example below i have a single application bar icon and a menu item and i try and talk to them from my code behind:

XAML Code:

<phone:PhoneApplicationPage.ApplicationBar>
    <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True">
        <shell:ApplicationBarIconButton 
            x:Name="btnIconButton"
            IconUri="/Images/appbar_button1.png" 
            Text="Button 1"
            />
        <shell:ApplicationBar.MenuItems>
            <shell:ApplicationBarMenuItem 
                Text="MenuItem 1"
                x:Name="btnMenuItem"
                />
        </shell:ApplicationBar.MenuItems>
    </shell:ApplicationBar>
</phone:PhoneApplicationPage.ApplicationBar>

Code behind:

public MainPage()
{
    InitializeComponent();
    this.Loaded += new RoutedEventHandler(MainPage_Loaded);
}
private void MainPage_Loaded(object sender, RoutedEventArgs e)
{
    // this will throw an exception:
    btnIconButton.Text = "bombs away!";

    // so will this:
    btnMenuItem.Text = "bombs away!";
}

image

The above code will throw a NullReferenceException because the control objects are not initialized and are Null.

This will also happen if you attempt to bind data to them:

<phone:PhoneApplicationPage.ApplicationBar>
    <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True">
        <shell:ApplicationBarIconButton 
            x:Name="btnIconButton"
            IconUri="/Images/appbar_button1.png" 
            Text="{Binding Button1Text}"
            />
    </shell:ApplicationBar>
</phone:PhoneApplicationPage.ApplicationBar>

But why?

You would think that if the ApplicationBar is a Silverlight control i should be able to bind, get properties, [insert magic coding thing]. Sadly though, the ApplicationBar is NOT a Silverlight control, but instead a wrapper object for a low level interop with the Windows Phone 7 OS:

private void MainPage_Loaded(object sender, RoutedEventArgs e)
{
    string AppBarType = ApplicationBar.GetType().ToString();
    // this is Microsoft.Phone.Shell.ApplicationBar who's base class is
    // Microsoft.Phone.Shell.Interop.NativeAppBarInteropWrapper 
}

While searching for a solution to the exceptions thrown above i came across the following blog post by Peter Torr on the Windows Phone 7 development team that further clarifies my assumption:

http://blogs.msdn.com/b/ptorr/archive/2010/06/18/why-are-the-applicationbar-objects-not-frameworkelements.aspx

“… Firstly, the ApplicationBar is not a Silverlight element and thus not all Silverlight concepts apply to it. For example, you would expect that the Background could be set to any kind of brush - SolidColorBrush, LinearGradientBrush, and so on - but because the AppBar is actually rendered by the Windows Phone shell, it only supports a solid colour. You might also expect to be able to put arbitrary Content inside the buttons, or to apply RenderTransforms to the menu items, and so on. And you should be able to put it anywhere on the screen with HorizontalAlignment / VerticalAlignment, right? Again, none of these things are supported by the underlying shell UX, so they would all have to throw exceptions or no-op when called. And where's the fun in that? …”

So what’s the solution

So you may be thinking:

“… Crap – my plans for ApplicationBar world domination have been thwarted…”

You would be wrong though, as some hours of banging my head against a wall later i found a solution, there is light at the end of the tunnel – world ApplicationBar domination awaits.

The solution is not to try and get your bindings etc to work, but to attack it from another angle – one that you CAN do: talk to the ApplicationBar’s list items directly.

This is possible as they are returned as simple object collections – you just cannot refer to them by their XAML x:Name:

private void MainPage_Loaded(object sender, RoutedEventArgs e)
{
    ApplicationBarIconButton button1 = ApplicationBar.Buttons[0] as ApplicationBarIconButton;
    if (button1 != null)
    {
        button1.Text = "p0wnage";
    }
    ApplicationBarMenuItem menuItem1 = ApplicationBar.MenuItems[0] as ApplicationBarMenuItem;
    if (menuItem1 != null)
    {
        menuItem1.Text = "Pure p0wnage";
    } 
}

image

Your grand Windows Phone 7 plans live on to see another day…