How to have binding on the ApplicationBar

The Windows Phone 7 API could display a standard menu bar in your silverlight applications. Unfortunately, it is not possible to make binding on it, because the object is not a FrameworkElement, but why? Here is the answer Peter Torr:

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?

Secondly, the content models in Silverlight 3 do not support controls with multiple collections of items. Some of the built-in elements have multiple collections - for example, Grid has the Children collection in addition to the RowDefinitions and ColumnDefinitions collections - but these are not actually usable outside of the "core" (they rely on PresentationFrameworkCollection, which is an internal class, and in order to maintain portability across platforms we are not adding anything phone-specific to the "core"). For the AppBar, we want both a Buttons collection and a MenuItems collection, and that's just not doable in a clean, strongly-typed API (there are hacks, such as making the AppBar a generic container and then hoping that developers only put buttons and menu items into it, but they're not good design). Silverlight 4 has addressed this limitation with the DependencyObjectCollection class, but Windows Phone is based on version 3, not 4.

To enable the binding, I developed a rapper that allows binding, this is very usefull when you use the MVVM pattern. It’s now possible to use ICommand on the applicationBarIconButton !!

<Controls:BindableApplicationBar x:Name="AppBar" BarOpacity="1.0" IsVisible="{Binding IsBarVisible}" >
  <Controls:BindableApplicationBarIconButton Command="{Binding AddCommand}" Text="Add" IconUri="/images/appbar.add.rest.png" />
  <Controls:BindableApplicationBar.MenuItems>
    <Controls:BindableApplicationBarMenuItem  Text="Settings" Command="{Binding SettingsCommand}" />
  </Controls:BindableApplicationBar.MenuItems>
</Controls:BindableApplicationBar>

Let's take a look on the wrapper. The wrapper is a control that inherits from ItemsControl and IApplicationBar  interface. I've named it BindableApplicationBar.

[ContentProperty("Buttons")]
public class BindableApplicationBar : ItemsControl, IApplicationBar
{
    // the wrapped ApplicationBar
    private readonly ApplicationBar _applicationBar;

    public BindableApplicationBar()
    {
        _applicationBar = new ApplicationBar();
        this.Loaded += BindableApplicationBar_Loaded;
    }

    void BindableApplicationBar_Loaded(object sender, RoutedEventArgs e)
    {
        // when the control is loaded,we search the PhoneApplicationPage to add the bar
        var page =
            this.GetVisualAncestors().Where(c => c is PhoneApplicationPage).FirstOrDefault() as PhoneApplicationPage;
        if (page != null) page.ApplicationBar = _applicationBar;
    }
...

The next step is to add the buttons and the menuitems XAML to our wrapped applicationbar.

protected override void OnItemsChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
    base.OnItemsChanged(e);
    // We clean all childrer...
    _applicationBar.Buttons.Clear();
    _applicationBar.MenuItems.Clear();
    // we take all children of the ItemsControl
    foreach (BindableApplicationBarIconButton button in Items.Where(c => c is BindableApplicationBarIconButton))
    {
        _applicationBar.Buttons.Add(button.Button);
    }
    foreach (BindableApplicationBarMenuItem button in Items.Where(c => c is BindableApplicationBarMenuItem))
    {
        _applicationBar.MenuItems.Add(button.MenuItem);
    }
}

Then, to make the binding effective, we simply add a DependencyProperty.

public static readonly DependencyProperty IsVisibleProperty =
    DependencyProperty.RegisterAttached("IsVisible", typeof(bool), typeof(BindableApplicationBar), new PropertyMetadata(true, OnVisibleChanged));

private static void OnVisibleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    if (e.NewValue != e.OldValue)
    {
        ((BindableApplicationBar) d)._applicationBar.IsVisible = (bool) e.NewValue;
    }
}

public bool IsVisible
{
    get { return (bool)GetValue(IsVisibleProperty); }
    set { SetValue(IsVisibleProperty, value); }
}

The wrapper mechanism is more or less identical to the BindableApplicationBarIconButton and the BindableApplicationBarMenuItem.

This code is used for the Warnygo project, and is of course available: Phone7.Fx.Preview.zip

If you have feedback, you are welcome !

UPDATE !

Use the latest version in http://phone7.codeplex.com

Hi there !

 

Specialized in .net technologies for many years, I am a technology fan in both asp.net and wpf/silverlight, using c# and .net 4.5

Taking advantages of new opportunities offered by the Windows Azure platform and WP/Win 8, I develop applications for Windows Phone, 5 of them are already available on the market place.


 

 

Month List